diff --git a/CHANGELOG.md b/CHANGELOG.md index c0a74be6f..d3f82a2f9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,11 @@ Magento Functional Testing Framework Changelog ================================================ +2.4.4 +----- +### Fixes +* Fixed an issue where `_CREDS` could not be resolved when used in a suite. + 2.4.3 ----- * Customizability diff --git a/bin/mftf b/bin/mftf index e45835696..0f2bf274d 100755 --- a/bin/mftf +++ b/bin/mftf @@ -29,7 +29,7 @@ try { try { $application = new Symfony\Component\Console\Application(); $application->setName('Magento Functional Testing Framework CLI'); - $application->setVersion('2.4.3'); + $application->setVersion('2.4.4'); /** @var \Magento\FunctionalTestingFramework\Console\CommandListInterface $commandList */ $commandList = new \Magento\FunctionalTestingFramework\Console\CommandList; foreach ($commandList->getCommands() as $command) { diff --git a/composer.json b/composer.json index a37dd06c7..66936938c 100755 --- a/composer.json +++ b/composer.json @@ -2,7 +2,7 @@ "name": "magento/magento2-functional-testing-framework", "description": "Magento2 Functional Testing Framework", "type": "library", - "version": "2.4.3", + "version": "2.4.4", "license": "AGPL-3.0", "keywords": ["magento", "automation", "functional", "testing"], "config": { diff --git a/composer.lock b/composer.lock index 5ce61bae1..10169fb08 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": "39e380b2acfd8adcd2c3642655f5a2a1", + "content-hash": "1b7f01a11ff57f3b64f08352c33a506f", "packages": [ { "name": "allure-framework/allure-codeception", diff --git a/dev/tests/verification/Resources/functionalSuiteHooks.txt b/dev/tests/verification/Resources/functionalSuiteHooks.txt index 4df805329..05ada44f1 100644 --- a/dev/tests/verification/Resources/functionalSuiteHooks.txt +++ b/dev/tests/verification/Resources/functionalSuiteHooks.txt @@ -3,6 +3,7 @@ namespace Group; use Magento\FunctionalTestingFramework\DataGenerator\Handlers\PersistedObjectHandler; +use Magento\FunctionalTestingFramework\DataGenerator\Handlers\CredentialStore; /** * Group class is Codeception Extension which is allowed to handle to all internal events. diff --git a/dev/tests/verification/Resources/functionalSuiteWithComments.txt b/dev/tests/verification/Resources/functionalSuiteWithComments.txt index d0e059aab..0e15b7909 100644 --- a/dev/tests/verification/Resources/functionalSuiteWithComments.txt +++ b/dev/tests/verification/Resources/functionalSuiteWithComments.txt @@ -3,6 +3,7 @@ namespace Group; use Magento\FunctionalTestingFramework\DataGenerator\Handlers\PersistedObjectHandler; +use Magento\FunctionalTestingFramework\DataGenerator\Handlers\CredentialStore; /** * Group class is Codeception Extension which is allowed to handle to all internal events. diff --git a/docs/data.md b/docs/data.md index 42736a6de..875df0198 100644 --- a/docs/data.md +++ b/docs/data.md @@ -121,6 +121,7 @@ The following conventions apply to MFTF ``: * A `` file may contain multiple data entities. * Camel case is used for `` elements. The name represents the `` type. For example, a file with customer data is `CustomerData.xml`. A file for simple product would be `SimpleProductData.xml`. * Camel case is used for the entity name. +* The file name must have the suffix `Data.xml`. ## Example @@ -263,4 +264,4 @@ Attributes|Type|Use|Description [Actions]: ./test/actions.md [category creation]: http://docs.magento.com/m2/ce/user_guide/catalog/category-create.html [Credentials]: ./credentials.md -[test actions]: ./test/actions.md#actions-returning-a-variable \ No newline at end of file +[test actions]: ./test/actions.md#actions-returning-a-variable diff --git a/docs/getting-started.md b/docs/getting-started.md index 2d3a6b8d9..450dbc2a6 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -100,7 +100,7 @@ location ~* ^/dev/tests/acceptance/utils($|/) { ## Set up an embedded MFTF {#setup-framework} -This is a default setup that you would need to start using the MFTF to cover your Magento project with functional testing. +This is the default setup of the MFTF that you would need to cover your Magento project with functional tests. It installs the framework using an existing Composer dependency such as `magento/magento2-functional-testing-framework`. If you want to set up the MFTF as a standalone tool, refer to [Set up a standalone MFTF][]. @@ -257,7 +257,7 @@ cd magento2-functional-testing-framework composer install ``` -### Step 3. Build the project {#build-project} +### Step 3. Build the project ```bash bin/mftf build:project @@ -309,7 +309,6 @@ allure serve dev/tests/_output/allure-results/ [allure docs]: https://docs.qameta.io/allure/ [Allure Framework]: http://allure.qatools.ru/ [basic configuration]: configuration.html#basic-configuration -[build]: #build-project [chrome driver]: https://sites.google.com/a/chromium.org/chromedriver/downloads [Codeception Test execution]: https://blog.jetbrains.com/phpstorm/2017/03/codeception-support-comes-to-phpstorm-2017-1/ [composer]: https://getcomposer.org/download/ diff --git a/docs/mftf-tests.md b/docs/mftf-tests.md new file mode 100644 index 000000000..3c4043d5b --- /dev/null +++ b/docs/mftf-tests.md @@ -0,0 +1,31 @@ + + +# MFTF functional test reference + +The Magento Functional Testing Framework runs tests on every Module within Magento. These files are stored within each Module folder in the Magento repo. +This page lists all those tests so that developers can have a good sense of what is covered. + +{% include mftf/mftf_data.md %} + +{% for item in mftf %} + +### {{ item.name }} +{% for file in item.items %} +#### [{{ file.filename }}]({{file.repo}}) +{: .mftf-test-link} + +{% for test in file.tests %} +{{test.testname}} + : {{test.description}} +{: .mftf-dl} +{% endfor %} +{% endfor %} +{% endfor %} \ No newline at end of file diff --git a/docs/selectors.md b/docs/selectors.md deleted file mode 100644 index 870072e15..000000000 --- a/docs/selectors.md +++ /dev/null @@ -1,35 +0,0 @@ -## Selectors - -These guidelines should help you to write high quality selectors. - -### Selectors SHOULD be written in CSS instead of XPath whenever possible - -CSS is generally easier to read than XPath. For example, `//*[@id="foo"]` in XPath can be expressed as simply as `#foo` in CSS. -See this [XPath Cheatsheet](https://devhints.io/xpath) for more examples. - -### XPath selectors SHOULD NOT use `@attribute="foo"`. - -This would fail if the attribute was `attribute="foo bar"`. -Instead you SHOULD use `contains(@attribute, "foo")` where `@attribute` is any valid attribute such as `@text` or `@class`. - -### CSS and XPath selectors SHOULD be implemented in their most simple form - -* GOOD: `#foo` -* BAD: `button[contains(@id, "foo")]` - -### CSS and XPath selectors SHOULD avoid making use of hardcoded indices - -Instead you SHOULD parameterize the selector. - -* GOOD: `.foo:nth-of-type({{index}})` -* BAD: `.foo:nth-of-type(1)` - -* GOOD: `button[contains(@id, "foo")][{{index}}]` -* BAD: `button[contains(@id, "foo")][1]` - -* GOOD: `#actions__{{index}}__aggregator` -* BAD: `#actions__1__aggregator` - -### CSS and XPath selectors MUST NOT reference the `@data-bind` attribute - -The `@data-bind` attribute is used by KnockoutJS, a framework Magento uses to create dynamic Javascript pages. Since this `@data-bind` attribute is tied to a specific framework, it should not be used for selectors. If Magento decides to use a different framework then these `@data-bind` selectors would break. diff --git a/docs/test-prep.md b/docs/test-prep.md new file mode 100644 index 000000000..b344fcf9f --- /dev/null +++ b/docs/test-prep.md @@ -0,0 +1,407 @@ +# Preparing a test for MFTF + +This tutorial demonstrates the process of converting a raw functional test into a properly abstracted test file, ready for publishing. + +## The abstraction process + +When first writing a test for a new piece of code such as a custom extension, it is likely that values are hardcoded for the specific testing environment while in development. To make the test more generic and easier for others to update and use, we need to abstract the test. +The general process: + +1. Convert the manual test to a working, hard-coded test. +1. Replace hardcoded selectors to a more flexible format such as [parameterized selectors][]. +1. Convert hardcoded form values and other data to data entities. +1. Convert [actions][] into [action groups][]. + +## The manual test + +Manual tests are just that: A series of manual steps to be run. + +```xml + + + + + + + + + + + + + + + +``` + +## The raw test + +This test works just fine. But it will only work if everything referenced in the test stays exactly the same. This neither reusable nor extensible. +Hardcoded selectors make it impossible to reuse sections in other action groups and tasks. They can also be brittle. If Magento happens to change a `class` or `id` on an element, the test will fail. + +Some data, like the SKU in this example, must be unique for every test run. Hardcoded values will fail here. [Data entities][] allow for `suffix` and `prefix` for ensuring unique data values. + +For our example, we have a test that creates a simple product. Note the hardcoded selectors, data values and lack of action groups. We will focus on the "product name". + +```xml + + + + + + + + + + <description value="Admin should be able to create simple product."/> + <severity value="MAJOR"/> + <group value="Catalog"/> + <group value="alex" /> + </annotations> + <before> + <!-- Login to Admin panel --> + <amOnPage url="admin" stepKey="openAdminPanelPage" /> + <fillField selector="#username" userInput="admin" stepKey="fillLoginField" /> + <fillField selector="#login" userInput="123123q" stepKey="fillPasswordField" /> + <click selector="#login-form .action-login" stepKey="clickLoginButton" /> + </before> + <after> + <!-- Logout from Admin panel --> + </after> + + <!-- Navigate to Catalog -> Products page (or just open by link) --> + <amOnPage url="admin/catalog/product/index" stepKey="openProductGridPage" /> + + <!-- Click "Add Product" button --> + <click selector="#add_new_product-button" stepKey="clickAddProductButton" /> + <waitForPageLoad stepKey="waitForNewProductPageOpened" /> + + <!-- Fill field "Name" with "Simple Product %unique_value%" --> + -----><fillField selector="input[name='product[name]']" userInput="Simple Product 12412431" stepKey="fillNameField" /> + + <!-- Fill field "SKU" with "simple_product_%unique_value%" --> + <fillField selector="input[name='product[sku]']" userInput="simple-product-12412431" stepKey="fillSKUField" /> + + <!-- Fill field "Price" with "500.00" --> + <fillField selector="input[name='product[price]']" userInput="500.00" stepKey="fillPriceField" /> + + <!-- Fill field "Quantity" with "100" --> + <fillField selector="input[name='product[quantity_and_stock_status][qty]']" userInput="100" stepKey="fillQtyField" /> + + <!-- Fill field "Weight" with "100" --> + <fillField selector="input[name='product[weight]']" userInput="100" stepKey="fillWeightField" /> + + ... + </test> +</tests> +``` + +## Extract the CSS selectors + +First we will extract the hardcoded CSS selector values into variables. +For instance: `input[name='product[name]']` becomes `{{AdminProductFormSection.productName}}`. +In this example `AdminProductFormSection` refers to the `<section>` in the XML file which contains an `<element>` node named `productName`. This element contains the value of the selector that was previously hardcoded: `input[name='product[name]']` + +```xml +<?xml version="1.0" encoding="UTF-8"?> +<!-- +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="CreateSimpleProductTest"> + <annotations> + <features value="Catalog"/> + <stories value="Create Product"/> + <title value="Admin should be able to create simple product."/> + <description value="Admin should be able to create simple product."/> + <severity value="MAJOR"/> + <group value="Catalog"/> + <group value="alex" /> + </annotations> + <before> + <before> + <!-- Login to Admin panel --> + <amOnPage url="admin" stepKey="openAdminPanelPage" /> + <fillField selector="#username" userInput="admin" stepKey="fillLoginField" /> + <fillField selector="#login" userInput="123123q" stepKey="fillPasswordField" /> + <click selector="#login-form .action-login" stepKey="clickLoginButton" /> + </before> + <after> + <!-- Logout from Admin panel --> + </after> + + <!-- Navigate to Catalog -> Products page (or just open by link) --> + <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="openProductGridPage" /> + + <!-- Click "Add Product" button --> + <click selector="{{AdminProductGridActionSection.addProductBtn}}" stepKey="clickAddProductButton" /> + <waitForPageLoad stepKey="waitForNewProductPageOpened" /> + + <!-- Fill field "Name" with "Simple Product %unique_value%" --> + -----><fillField selector="{{AdminProductFormSection.productName}}" userInput="Simple Product 12412431" stepKey="fillNameField" /> + <!-- Fill field "SKU" with "simple_product_%unique_value%" --> + <fillField selector="{{AdminProductFormSection.productSku}}" userInput="simple-product-12412431" stepKey="fillSKUField" /> + + <!-- Fill field "Price" with "500.00" --> + <fillField selector="{{AdminProductFormSection.productPrice}}" userInput="500.00" stepKey="fillPriceField" /> + + <!-- Fill field "Quantity" with "100" --> + <fillField selector="{{AdminProductFormSection.productQuantity}}" userInput="100" stepKey="fillQtyField" /> + + <!-- Fill field "Weight" with "100" --> + <fillField selector="{{AdminProductFormSection.productWeight}}" userInput="100" stepKey="fillWeightField" /> + ... + </test> +</tests> +``` + +## The section file + +We abstract these selector values to a file named `AdminProductFormSection.xml` which is kept in the `Section` folder. +Within this file, there can be multiple `<section>` nodes which contains data for different parts of the test. +Here we are interested in `<section name="AdminProductFormSection">`, where we are keeping our extracted values from above. + +```xml +<?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="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="input[name='product[name]']"/> + <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="input[name='product[sku]']"/> + <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]']"/> + ... + </section> + <section name="ProductInWebsitesSection"> + <element name="sectionHeader" type="button" selector="div[data-index='websites']" timeout="30"/> + <element name="website" type="checkbox" selector="//label[contains(text(), '{{var1}}')]/parent::div//input[@type='checkbox']" parameterized="true"/> + </section> +``` + +## Data entities + +The hardcoded values of these form elements are abstracted to a "data entity" XML file. +We replace the hardcoded values with variables and the MFTF will do the variable substitution. + +```xml +<?xml version="1.0" encoding="UTF-8"?> +<!-- +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="CreateSimpleProductTest"> + <annotations> + <features value="Catalog"/> + <stories value="Create Product"/> + <title value="Admin should be able to create simple product."/> + <description value="Admin should be able to create simple product."/> + <severity value="MAJOR"/> + <group value="Catalog"/> + <group value="alex" /> + </annotations> + <before> + <!-- Login to Admin panel --> + <amOnPage url="admin" stepKey="openAdminPanelPage" /> + <fillField selector="#username" userInput="admin" stepKey="fillLoginField" /> + <fillField selector="#login" userInput="123123q" stepKey="fillPasswordField" /> + <click selector="#login-form .action-login" stepKey="clickLoginButton" /> + </before> + <after> + <!-- Logout from Admin panel --> + </after> + + <!-- Navigate to Catalog -> Products page (or just open by link) --> + <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="openProductGridPage" /> + + <!-- Click "Add Product" button --> + <click selector="{{AdminProductGridActionSection.addProductBtn}}" stepKey="clickAddProductButton" /> + <waitForPageLoad stepKey="waitForNewProductPageOpened" /> + + <!-- Fill field "Name" with "Simple Product %unique_value%" --> + ----><fillField selector="{{AdminProductFormSection.productName}}" userInput="{{_defaultProduct.name}}" stepKey="fillNameField" /> + + <!-- Fill field "SKU" with "simple_product_%unique_value%" --> + <fillField selector="{{AdminProductFormSection.productSku}}" userInput="{{_defaultProduct.sku}}" stepKey="fillSKUField" /> + + <!-- Fill field "Price" with "500.00" --> + <fillField selector="{{AdminProductFormSection.productPrice}}" userInput="{{_defaultProduct.price}}" stepKey="fillPriceField" /> + + <!-- Fill field "Quantity" with "100" --> + <fillField selector="{{AdminProductFormSection.productQuantity}}" userInput="{{_defaultProduct.quantity}}" stepKey="fillQtyField" /> + + <!-- Fill field "Weight" with "100" --> + <fillField selector="{{AdminProductFormSection.productWeight}}" userInput="{{_defaultProduct.weight}}" stepKey="fillWeightField" /> + + ... + </test> +</tests> +``` + +One of the reasons that we abstract things is so that they are more flexible and reusable. In this case, we can leverage this flexibility and use an existing data file. For this scenario, we are using [this file](https://raw.githubusercontent.com/magento-pangolin/magento2/MageTestFest/app/code/Magento/Catalog/Test/Mftf/Data/ProductData.xml). + +Data entities are important because this is where a `suffix` or `prefix` can be defined. This ensures that data values can be unique for every test run. + +Notice that the `<entity>` name is `_defaultProduct` as referenced above. Within this entity is the `name` value. + +```xml +<entities xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataProfileSchema.xsd"> + <entity name="_defaultProduct" type="product"> + <data key="sku" unique="suffix">testSku</data> + <data key="type_id">simple</data> + <data key="attribute_set_id">4</data> + <data key="visibility">4</data> + ---> <data key="name" unique="suffix">testProductName</data> + <data key="price">123.00</data> + <data key="urlKey" unique="suffix">testurlkey</data> + <data key="status">1</data> + <data key="quantity">100</data> + <data key="weight">1</data> + <requiredEntity type="product_extension_attribute">EavStockItem</requiredEntity> + <requiredEntity type="custom_attribute_array">CustomAttributeCategoryIds</requiredEntity> + </entity> +``` + +The `unique="suffix"` attribute appends a random numeric string to the end of the actual data string. This ensures that unique values are used for each test run. +See [Input testing data][] for more information. + +## Convert actions to action groups + +Action groups are sets of steps that are run together. Action groups are designed to break up multiple individual steps into logical groups. For example: logging into the admin panel requires ensuring the login form exists, filling in two form fields and clicking the **Submit** button. These can be bundled into a single, reusable "LoginAsAdmin" action group that can be applied to any other test. This leverages existing code and prevents duplication of effort. We recommend that all steps in a test be within an action group. + +Using action groups can be very useful when testing extensions. +Extending the example above, assume the first extension adds a new field to the admin log in, a Captcha for example. +The second extension we are testing needs to log in AND get past the Captcha. + +1. The admin login is encapsulated in an action group. +2. The Captcha extension properly extends the `LoginAsAdmin` capture group using the `merge` functionality. +3. Now the second extension can call the `LoginAsAdmin` action group and because of the `merge`, it will automatically include the Captcha field. + +In this case, the action group is both reusable and extensible! + +We further abstract the test by putting these action groups in their own file: ['app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductActionGroup.xml'](https://raw.githubusercontent.com/magento-pangolin/magento2/e5671d84aa63cad772fbba757005b3d89ddb79d9/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductActionGroup.xml) + +To create an action group, take the steps and put them within an `<actionGroup>` element. Note that the `<argument>` node defines the `_defaultProduct` data entity that is required for the action group. + +```xml +<?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 main fields in create product form--> + <actionGroup name="fillMainProductForm"> + <arguments> + <argument name="product" defaultValue="_defaultProduct"/> + </arguments> + -----><fillField selector="{{AdminProductFormSection.productName}}" userInput="{{product.name}}" stepKey="fillProductName"/> + <fillField selector="{{AdminProductFormSection.productSku}}" userInput="{{product.sku}}" stepKey="fillProductSku"/> + <fillField selector="{{AdminProductFormSection.productPrice}}" userInput="{{product.price}}" stepKey="fillProductPrice"/> + <fillField selector="{{AdminProductFormSection.productQuantity}}" userInput="{{product.quantity}}" stepKey="fillProductQty"/> + <selectOption selector="{{AdminProductFormSection.productStockStatus}}" userInput="{{product.status}}" stepKey="selectStockStatus"/> + <selectOption selector="{{AdminProductFormSection.productWeightSelect}}" userInput="This item has weight" stepKey="selectWeight"/> + <fillField selector="{{AdminProductFormSection.productWeight}}" userInput="{{product.weight}}" stepKey="fillProductWeight"/> + <click selector="{{AdminProductSEOSection.sectionHeader}}" stepKey="openSeoSection"/> + <fillField userInput="{{product.urlKey}}" selector="{{AdminProductSEOSection.urlKeyInput}}" stepKey="fillUrlKey"/> + </actionGroup> +``` + +Note how the `<argument>` node takes in the `_defaultProduct` data entity and renames it to `product`, which is then used for the `userInput` values. + +Now we can reference this action group within our test (and any other test). + +```xml +<?xml version="1.0" encoding="UTF-8"?> +<!-- +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="CreateSimpleProductTest"> + <annotations> + <features value="Catalog"/> + <stories value="Create Product"/> + <title value="Admin should be able to create simple product."/> + <description value="Admin should be able to create simple product."/> + <severity value="MAJOR"/> + <group value="Catalog"/> + <group value="alex" /> + </annotations> + <before> + <!-- Login to Admin panel --> + <actionGroup ref="LoginAsAdmin" stepKey="loginToAdminPanel" /> + </before> + <after> + <!-- Logout from Admin panel --> + </after> + + <!-- Navigate to Catalog -> Products page (or just open by link) --> + <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="openProductGridPage" /> + <waitForPageLoad stepKey="waitForProductGridPageLoaded" /> + + <!-- Click "Add Product" button --> + <actionGroup ref="goToCreateProductPage" stepKey="goToCreateProductPage" /> + -----><fillField selector="{{AdminProductFormSection.productName}}" userInput="{{product.name}}" stepKey="fillProductName"/> + <actionGroup ref="fillMainProductForm" stepKey="fillProductForm"> + <argument name="product" value="_defaultProduct" /> + </actionGroup> + + <!-- See success save message "You saved the product." --> + <actionGroup ref="saveProductForm" stepKey="clickSaveOnProductForm" /> + + <actionGroup ref="AssertProductInGridActionGroup" stepKey="assertProductInGrid" /> + + <!-- Open Storefront Product Page and verify "Name", "SKU", "Price" --> + <actionGroup ref="AssertProductInStorefrontProductPage" stepKey="assertProductInStorefrontProductPage"> + <argument name="product" value="_defaultProduct" /> + </actionGroup> + </test> +</tests> +``` + +A well written test will end up being a set of action groups. +The finished test is fully abstracted in such a way that it is short and readable and importantly, the abstracted data and action groups can be used again. + +<!-- Link Definitions --> +[actions]: https://devdocs.magento.com/mftf/docs/test/actions.html +[action groups]: https://devdocs.magento.com/mftf/docs/test/action-groups.html +[Data entities]: https://devdocs.magento.com/mftf/docs/data.html +[Input testing data]: https://devdocs.magento.com/mftf/docs/data.html +[parameterized selectors]: https://devdocs.magento.com/mftf/docs/section/parameterized-selectors.html diff --git a/docs/versioning.md b/docs/versioning.md index 660b88a40..d018058d5 100644 --- a/docs/versioning.md +++ b/docs/versioning.md @@ -1,8 +1,8 @@ -# Versioning +# MFTF versioning schema -This documemt describes the versioning policy for the Magento Functional Testing Framework (MFTF), including the version numbering schema. +This document describes the versioning policy for the Magento Functional Testing Framework (MFTF), including the version numbering schema. -## Backward Compatibility +## Backward compatibility In this context, backward compatibility means that when changes are made to the MFTF, all existing tests still run normally. If a modification to MFTF forces tests to be changed, this is a backward incompatible change. @@ -62,7 +62,7 @@ This table lists the version of the MFTF that was released with a particular ver |Magento version| MFTF version| |---|---| +| 2.3.2 | 2.3.14 | | 2.3.1 | 2.3.13 | | 2.3.0 | 2.3.9 | | 2.2.8 | 2.3.13 | -| 2.2.7 | 2.3.8 | diff --git a/etc/config/command.php b/etc/config/command.php index 047af324a..f018e3014 100644 --- a/etc/config/command.php +++ b/etc/config/command.php @@ -12,8 +12,8 @@ $tokenModel = $magentoObjectManager->get(\Magento\Integration\Model\Oauth\Token::class); $tokenPassedIn = urldecode($_POST['token']); - $command = urldecode($_POST['command']); - $arguments = urldecode($_POST['arguments']); + $command = str_replace([';', '&', '|'], '', urldecode($_POST['command'])); + $arguments = str_replace([';', '&', '|'], '', urldecode($_POST['arguments'])); // Token returned will be null if the token we passed in is invalid $tokenFromMagento = $tokenModel->loadByToken($tokenPassedIn)->getToken(); diff --git a/src/Magento/FunctionalTestingFramework/Suite/views/SuiteClass.mustache b/src/Magento/FunctionalTestingFramework/Suite/views/SuiteClass.mustache index 58c0cbf0f..b98405c03 100644 --- a/src/Magento/FunctionalTestingFramework/Suite/views/SuiteClass.mustache +++ b/src/Magento/FunctionalTestingFramework/Suite/views/SuiteClass.mustache @@ -3,6 +3,7 @@ namespace Group; use Magento\FunctionalTestingFramework\DataGenerator\Handlers\PersistedObjectHandler; +use Magento\FunctionalTestingFramework\DataGenerator\Handlers\CredentialStore; /** * Group class is Codeception Extension which is allowed to handle to all internal events.