From 2a8c17a3b2a3bb19393a045b6e5a32b170d75853 Mon Sep 17 00:00:00 2001 From: Donald Booth Date: Tue, 5 Mar 2019 16:58:47 -0600 Subject: [PATCH 01/18] Adding docs folder. --- docs/best-practices.md | 168 ++ docs/commands/codeception.md | 91 + docs/commands/mftf.md | 410 +++ docs/configuration.md | 265 ++ docs/credentials.md | 101 + docs/data.md | 266 ++ docs/debugging.md | 40 + docs/extending.md | 372 +++ docs/getting-started.md | 311 ++ .../catalogCategoryRepository-operations.png | Bin 0 -> 20478 bytes docs/img/data-dia.svg | 489 ++++ docs/img/issue.png | Bin 0 -> 18617 bytes docs/img/metadata-dia.svg | 1050 +++++++ docs/img/mftf-fork.gif | Bin 0 -> 420980 bytes docs/img/page-dia.svg | 290 ++ docs/img/pull-request.png | Bin 0 -> 10372 bytes docs/img/section-dia.svg | 296 ++ docs/img/switching-the-base.png | Bin 0 -> 9796 bytes docs/img/test-dia.svg | 1228 ++++++++ docs/img/trouble-chrome232.png | Bin 0 -> 49401 bytes docs/include/note-2.2-docs.md | 19 + docs/introduction.md | 141 + docs/merging.md | 575 ++++ docs/metadata.md | 598 ++++ docs/page.md | 185 ++ docs/reporting.md | 359 +++ docs/section.md | 151 + docs/section/locator-functions.md | 48 + docs/section/parameterized-selectors.md | 160 + docs/suite.md | 305 ++ docs/test.md | 159 + docs/test/action-groups.md | 270 ++ docs/test/actions.md | 2590 +++++++++++++++++ docs/test/annotations.md | 237 ++ docs/test/assertions.md | 584 ++++ docs/test/img/action-groups-dia.svg | 516 ++++ docs/tips-tricks.md | 382 +++ docs/troubleshooting.md | 64 + docs/update.md | 82 + docs/versioning.md | 57 + 40 files changed, 12859 insertions(+) create mode 100644 docs/best-practices.md create mode 100644 docs/commands/codeception.md create mode 100644 docs/commands/mftf.md create mode 100644 docs/configuration.md create mode 100644 docs/credentials.md create mode 100644 docs/data.md create mode 100644 docs/debugging.md create mode 100644 docs/extending.md create mode 100644 docs/getting-started.md create mode 100644 docs/img/catalogCategoryRepository-operations.png create mode 100644 docs/img/data-dia.svg create mode 100644 docs/img/issue.png create mode 100644 docs/img/metadata-dia.svg create mode 100644 docs/img/mftf-fork.gif create mode 100644 docs/img/page-dia.svg create mode 100644 docs/img/pull-request.png create mode 100644 docs/img/section-dia.svg create mode 100644 docs/img/switching-the-base.png create mode 100644 docs/img/test-dia.svg create mode 100644 docs/img/trouble-chrome232.png create mode 100644 docs/include/note-2.2-docs.md create mode 100644 docs/introduction.md create mode 100644 docs/merging.md create mode 100644 docs/metadata.md create mode 100644 docs/page.md create mode 100644 docs/reporting.md create mode 100644 docs/section.md create mode 100644 docs/section/locator-functions.md create mode 100644 docs/section/parameterized-selectors.md create mode 100644 docs/suite.md create mode 100644 docs/test.md create mode 100644 docs/test/action-groups.md create mode 100644 docs/test/actions.md create mode 100644 docs/test/annotations.md create mode 100644 docs/test/assertions.md create mode 100644 docs/test/img/action-groups-dia.svg create mode 100644 docs/tips-tricks.md create mode 100644 docs/troubleshooting.md create mode 100644 docs/update.md create mode 100644 docs/versioning.md diff --git a/docs/best-practices.md b/docs/best-practices.md new file mode 100644 index 000000000..013c5e7be --- /dev/null +++ b/docs/best-practices.md @@ -0,0 +1,168 @@ +--- +mftf-release: 2.3.0 +redirect_from: /guides/v2.3/magento-functional-testing-framework/2.3/best-practices.html +--- + +# Best practices + +_This topic was updated due to the {{ page.mftf-release }} MFTF release._ +{: style="text-align: right"} + +Check out our best practices below to ensure you are getting the absolute most out of the Magento Functional Testing Framework. + +## Action group + +1. [Action group] names should be sufficiently descriptive to inform a test writer of what the action group does and when it should be used. + Add additional explanation in comments if needed. +2. Provide default values for the arguments that apply to your most common case scenarios. + +## `actionGroups` vs `extends` + +Use an action group to wraps a set of actions to reuse them multiple times. + +Use an [extension] when a test or action group needs to be repeated with the exception of a few steps. + +### When to use `extends` + +Use `extends` in your new test or action group when at least one of the following conditions is applicable to your case: + +1. You want to keep the original test without any modifications. +2. You want to create a new test that follows the same path as the original test. +3. You want a new action group that behaves similarly to the existing action group, but you do not want to change the functionality of the original action group. + +### When to avoid `extends` + +Do not use `extends` in the following conditions: + +1. You want to change the functionality of the test or action group and do not need to run the original version. +2. You plan to merge the base test or action group. + +The following pattern is used when merging with `extends`: + +1. The original test is merged. +2. The extended test is created from the merged original test. +3. The extended test is merged. + +## Annotation + +1. Use [annotations] in a test. +2. Update your annotations correspondingly when updating tests. + +## Data entity + +1. Keep your testing instance clean. + Remove data after the test if the test required creating any data. + Use a corresponding [``] test step in your [``] block when using a [``] action in a [``] block. +2. Make specific data entries under test to be unique. + Enable data uniqueness where data values are required to be unique in a database by test design. + Use `unique=”suffix”` or `unique=”prefix”` to append or prepend a unique value to the [entity] attribute. + This ensures that tests using the entity can be repeated. +3. Do not modify existing data entity fields or merge additional data fields without complete understanding and verifying the usage of existing data in tests. + Create a new data entity for your test if you are not sure. + +## Naming conventions + +### File names + +Name files according to the following patterns to make searching in future more easy: + +#### Test file name + +Format: {_Admin_ or _Storefront_}{Functionality}_Test.xml_, where Functionality briefly describes the testing functionality. + +Example: _StorefrontCreateCustomerTest.xml_. + +#### Section file name + +Format: {_Admin_ or _Storefront_}{UI Description}_Section.xml_, where UI Description briefly describes the testing UI. + +Example: _AdminNavbarSection.xml_. + +#### Data file name + +Format: {Type}_Data.xml_, where Type represents the entity type. + +Example: _ProductData.xml_. + +### Object names + +Use the _Foo.camelCase_ naming convention, which is similar to _Classes_ and _classProperties_ in PHP. + +#### Upper case + +Use an upper case first letter for: + +- File names. Example: _StorefrontCreateCustomerTest.xml_ +- Test name attributes. Example: ``. +- Data entity names. Example: ``. +- Page name. Example: ``. +- Section name. Example: `
`. +- Action group name. Example: ``. + +#### Lower case + +Use a lower case first letter for: + +- Data keys. Example: ``. +- Element names. Examples: ``. + +## Page object + +Use [parameterized selectors] for constructing a selector when test specific or runtime generated information is needed. +Do not use them for static elements. + +{:style="color:red"} +BAD: + +``` xml + +``` + +{:style="color:green"} +GOOD: + +Define these three elements and reference them by name in the tests. + +``` xml + + + +``` + +## Test + +1. Use actions such as [``], [``], and [``] to wait the exact time required for the test step. + Try to avoid using the [``] action, because it forces the test to wait for the time you specify. You may not need to wait so long to proceed. +1. Keep your tests short and granular for target testing, easier reviews, and easier merge conflict resolution. + It also helps you to identify the cause of test failure. +1. Use comments to keep tests readable and maintainable: + - Keep the inline `` and [``] tags up to date. + It helps to inform the reader of what you are testing and to yield a more descriptive Allure report. + - Explain in comments unclear or tricky test steps. +1. Refer to [sections] instead of writing selectors. + +## Test step merging order + +When setting a [merging] order for a test step, do not depend on steps from Magento modules that could be disabled by an application. + +For example, when you write a test step to create a gift card product, set your test step **after** simple product creation and let the MFTF handle the merge order. +Since the configurable product module could be disabled, this approach is more reliable than setting the test step **before** creating a configurable product. + + + +[``]: test/actions.html#before-and-after +[``]: test/actions.html#before-and-after +[``]: test/actions.html#comment +[``]: test/actions.html#createdata +[``]: test/actions.html#deletedata +[``]: test/actions.html#wait +[``]: test/actions.html#waitforelement +[``]: test/actions.html#waitforelementvisible +[``]: test/actions.html#waitforloadingmasktodisappear +[Action group]: test/action-groups.html +[annotations]: test/annotations.html +[entity]: data.html +[extension]: extending.html +[merging]: merging.html +[parameterized selectors]: section/parameterized-selectors.html +[sections]: section.html diff --git a/docs/commands/codeception.md b/docs/commands/codeception.md new file mode 100644 index 000000000..7d0ac7b0d --- /dev/null +++ b/docs/commands/codeception.md @@ -0,0 +1,91 @@ +--- +mftf-release: 2.3.0 +redirect_from: /guides/v2.3/magento-functional-testing-framework/2.3/commands/codeception.html +--- + +# CLI commands: vendor/bin/codecept + +_This topic was updated due to the {{page.mftf-release}} MFTF release._ +{: style="text-align: right"} + +{:.bs-callout .bs-callout-warning} +We do not recommend using Codeception commands directly as they can break the MFTF basic workflow. +All the Codeception commands you need are wrapped using the [`mftf` tool][]. + +To run the Codeception testing framework commands directly, change your directory to the ``. + +## Usage examples + +Run all the generated tests: + +```bash +vendor/bin/codecept run functional +``` + +Run all tests without the `` [annotation][]: + +```bash +vendor/bin/codecept run functional --skip-group skip +``` + +Run all tests with the `` [annotation][] but with no ``: + +```bash +vendor/bin/codecept run functional --group example --skip-group skip +``` + +## `codecept run` + +`codecept run` runs the test suites: + +```bash +vendor/bin/codecept run +``` + +{: .bs-callout .bs-callout-info } +The following documentation corresponds to Codeception 2.3.8. + +```bash +Full reference: + +Arguments: + suite suite to be tested + test test to be run + +Options: + -o, --override=OVERRIDE Override config values (multiple values allowed) + --config (-c) Use custom path for config + --report Show output in compact style + --html Generate html with results (default: "report.html") + --xml Generate JUnit XML Log (default: "report.xml") + --tap Generate Tap Log (default: "report.tap.log") + --json Generate Json Log (default: "report.json") + --colors Use colors in output + --no-colors Force no colors in output (useful to override config file) + --silent Only outputs suite names and final results + --steps Show steps in output + --debug (-d) Show debug and scenario output + --coverage Run with code coverage (default: "coverage.serialized") + --coverage-html Generate CodeCoverage HTML report in path (default: "coverage") + --coverage-xml Generate CodeCoverage XML report in file (default: "coverage.xml") + --coverage-text Generate CodeCoverage text report in file (default: "coverage.txt") + --coverage-phpunit Generate CodeCoverage PHPUnit report in file (default: "coverage-phpunit") + --no-exit Don't finish with exit code + --group (-g) Groups of tests to be executed (multiple values allowed) + --skip (-s) Skip selected suites (multiple values allowed) + --skip-group (-x) Skip selected groups (multiple values allowed) + --env Run tests in selected environments. (multiple values allowed, environments can be merged with ',') + --fail-fast (-f) Stop after first failure + --help (-h) Display this help message. + --quiet (-q) Do not output any message. + --verbose (-v|vv|vvv) Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug + --version (-V) Display this application version. + --ansi Force ANSI output. + --no-ansi Disable ANSI output. + --no-interaction (-n) Do not ask any interactive question. +``` + + + +[`mftf` tool]: mftf.html +[annotation]: ../test/annotations.html \ No newline at end of file diff --git a/docs/commands/mftf.md b/docs/commands/mftf.md new file mode 100644 index 000000000..b5617d4b3 --- /dev/null +++ b/docs/commands/mftf.md @@ -0,0 +1,410 @@ +--- +mftf-release: 2.3.11 +redirect_from: /guides/v2.3/magento-functional-testing-framework/2.3/commands/mftf.html +--- + +# CLI commands: vendor/bin/mftf + +_This topic was updated due to the {{page.mftf-release}} MFTF release._ +{: style="text-align: right"} + +The Magento Functional Testing Framework (MFTF) introduces the command line interface (CLI) tool `vendor/bin/mftf` to facilitate your interaction with the framework. + +{% +include note.html +type='info' +content='`mftf` commands replace the `robo` commands that were used in previous releases.' +%} + +## Command format + +In the project root directory (where you have installed the framework as a composer dependency), run commands using the following format: + +```bash +vendor/bin/mftf command [options] [] [--remove|-r] +``` + +## Useful commands + +Use the following commands to run commonly performed tasks. + +### Apply the configuration parameters + +```bash +vendor/bin/mftf build:project +``` + +### Upgrade the project + +```bash +vendor/bin/mftf build:project --upgrade +``` + +Upgrades the existing MFTF tests after the MFTF major upgrade. + +### Generate all tests + +```bash +vendor/bin/mftf generate:tests +``` + +### Generate tests by test name + +```bash +vendor/bin/mftf generate:tests LoginAsAdminTest LoginAsCustomerTest +``` + +### Generate and run the tests for a specified group + +```bash +vendor/bin/mftf run:group product -r +``` + +This command cleans up the previously generated tests; generates and runs tests for the product group (where `group="product"`). + +### Generate and run particular tests + +```bash +vendor/bin/mftf run:test LoginAsAdminTest LoginAsCustomerTest -r +``` + +This command cleans up the previously generated tests; generates and runs the `LoginAsAdminTest` and `LoginAsCustomerTest` tests. + +### Generate and run previously failed tests + +```bash +vendor/bin/mftf run:failed +``` + +This command cleans up the previously generated tests; generates and runs the tests listed in `dev/tests/acceptance/tests/_output/failed`. +For more details about `failed`, refer to [Reporting][]. + +## Reference + +### `build:project` + +#### Description + +Clone the example configuration files and build the Codeception project. + +#### Usage + +```bash +vendor/bin/mftf build:project [--upgrade] [config_param_options] +``` + +#### Options + +| Option | Description | +| ----------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `-u`, `--upgrade` | Upgrades existing MFTF tests according to requirements of the last major release. Specifying this flag upgrades only those tests in the default location. Example: `build:project --upgrade`. | + +You can include options to set configuration parameter values for your environment since the project build process also [sets up the environment][setup]. + +```bash +vendor/bin/mftf build:project --MAGENTO_BASE_URL=http://magento.local/ --MAGENTO_BACKEND_NAME=admin214365 +``` + +### `generate:tests` + +#### Description + +Generate PHP code from the tests defined in XML files. +The path is set in the `TESTS_MODULE_PATH` [configuration] parameter. + +#### Usage + +```bash +vendor/bin/mftf generate:tests [option] [] [] [--remove] +``` + +#### Options + +| Option | Description | +| --------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `--config=[||]` | Creates a single manifest file with a list of all tests. The default location is `tests/functional/Magento/FunctionalTest/_generated/testManifest.txt`.
You can split the list into multiple groups using `--config=parallel`; the groups will be generated in `_generated/groups/` like `_generated/groups/group1.txt, group2.txt, ...`.
Available values: `default` (default), `singleRun`(same as `default`), and `parallel`.
Example: `generate:tests --config=parallel`. | +| `--force` | Forces test generation, regardless of the module merge order defined in the Magento instance. Example: `generate:tests --force`. | +| `-i,--time` | Set time in minutes to determine the group size when `--config=parallel` is used. The __default value__ is `10`. Example: `generate:tests --config=parallel --time=15` | +| `--tests` | Defines the test configuration as a JSON string. | +| `--debug` | Returns additional debug information (such as the filename where an error occurred) when test generation fails because of an invalid XML schema. This parameter takes extra processing time. Use it after test generation has failed once. | +| `-r,--remove` | Removes the existing generated suites and tests cleaning up the `_generated` directory before the actual run. For example, `generate:tests SampleTest --remove` cleans up the entire `_generated` directory and generates `SampleTest` only. | + +#### Examples of the JSON configuration + +The configuration to generate a single test with no suites: + +```json +{ + "tests":[ + "general_test1" //Generate the "general_test1" test. + ], + "suites": null +} +``` + +The configuration to generate a single test in the suite: + +```json +{ + "tests": null, // No tests outside the suite configuration will be generated. + "suites":{ + "sample":[ // The suite that contains the test. + "suite_test1" // The test to be generated. + ] + } +} +``` + +Complex configuration to generate a few non-suite tests, a single test in a suite, and an entire suite: + +```json +{ + "tests":[ + "general_test1", + "general_test2", + "general_test3" + ], + "suites":{ //Go to suites. + "sample":[ //Go to the "sample" suite. + "suite_test1" //Generate the "suite_test1" test. + ], + "sample2":[] //Generate all tests in the "sample2" suite. + } +} +``` + +The command that encodes this complex configuration: + +```bash +vendor/bin/mftf generate:tests --tests "{\r\n\"tests\":[\r\n\"general_test1\",\r\n\"general_test2\",\r\n\"general_test3\"\r\n],\r\n\"suites\":{\r\n\"sample\":[\r\n\"suite_test1\"\r\n],\r\n\"sample2\":null\r\n}\r\n}" +``` + +{% include note.html +type='info' +content='The strings must be escaped and surrounded in quotes.' +%} + +### `generate:suite` + +#### Description + +Generates one or more suites based on XML declarations. + +#### Usage + +```bash +vendor/bin/mftf generate:suite [] [--remove] +``` + +#### Options + +| Option | Description | +| ------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| `-r,--remove` | Removes the existing generated suites and tests cleaning up the `_generated` directory before the actual run. For example, `vendor/bin/mftf generate:suite WYSIWYG --remove` cleans up the entire `_generated` directory and generates `WYSIWYG` only. | + +#### Example + +```bash +vendor/bin/mftf generate:suite suite1 suite2 +``` + +### `generate:urn-catalog` + +#### Description + +Generates a URN catalog, enabling PhpStorm to recognize and highlight URNs. +It also enables auto-completion in PhpStorm. + +#### Usage + +```bash +vendor/bin/mftf generate:urn-catalog [--force] [] +``` + +`misc.xml` is typically located in `/.idea/`. + +#### Options + +| Option | Description | +| ------------- | --------------------------------------------------------------------- | +| `-f, --force` | Creates the `misc.xml` file if it does not exist in the given `path`. | + +#### Example + +```bash +vendor/bin/mftf generate:urn-catalog .idea/ +``` + +### `reset` + +#### Description + +Cleans any configuration files and generated artifacts from the environment. +The `.env` file is not affected. + +#### Usage + +```bash +vendor/bin/mftf reset [--hard] +``` + +#### Options + +| Option | Description | +| -------- | ------------------------------------------ | +| `--hard` | Forces a reset of the configuration files. | + +#### Example + +```bash +vendor/bin/mftf reset --hard +``` + +### `run:group` + +Generates and executes the listed groups of tests using Codeception. + +#### Usage + +```bash +vendor/bin/mftf run:group [--skip-generate|--remove] [--] [] +``` + +#### Options + +| Option | Description | +| --------------------- | --------------------------------------------------------------------------------------------------------- | +| `-k, --skip-generate` | Skips generating from the source XML. Instead, the command executes previously-generated groups of tests. | +| `-r, --remove` | Removes previously generated suites and tests before the actual generation and run. | + +#### Examples + +Clean up after the last test run; generate from XML and execute the tests with the annotations `group="group1"` and `group="group2"`: + +```bash +vendor/bin/mftf -r -- run:group group1 group2 +``` + +Execute previously generated tests with the annotations `group="group1"` and `group="group2"`, skipping the regeneration of the test: + +```bash +vendor/bin/mftf run:group -k -- group1 group2 +``` + +### `run:test` + +Generates and executes tests by name using Codeception. + +#### Usage + +```bash +vendor/bin/mftf run:test [--skip-generate|--remove] [--] [] +``` + +#### Options + +| Option | Description | +|-----------------------|-----------------------------------------------------------------------------------------------------------| +| `-k, --skip-generate` | Skips generating from the source XML. Instead, the command executes previously-generated groups of tests. | +| `-r, --remove` | Remove previously generated suites and tests. | + +#### Examples + +Generate the `LoginCustomerTest` and `StorefrontCreateCustomerTest` tests from XML and execute all the generated tests: + +```bash +vendor/bin/mftf run:test LoginCustomerTest StorefrontCreateCustomerTest +``` + +### `run:failed` + +Regenerates and reruns tests that previously failed. + +This command cleans up previously generated tests. It generates and runs the tests listed in `dev/tests/acceptance/tests/_output/failed`. +For more details about `failed`, refer to [Reporting][]. + +#### Usage + +```bash +vendor/bin/mftf run:failed +``` + +#### Examples + +Run the tests that failed in the previous run: + +```bash +vendor/bin/mftf run:failed +``` + +### `setup:env` + +Updates the [configuration] parameter values in the [`.env`] file. +Creates the `.env` file if it does not exist. + +#### Usage + +```bash +vendor/bin/mftf setup:env [config_param_option1=] [config_param_option2=] +``` + +`config_param` is a configuration parameter from the `.env` file. +The command consumes the parameters in a format of options assigned with values, for example `--MAGENTO_BASE_URL=http://magento.local/`. +If you specify a parameter that the `.env` file does not contain, the command returns an error. + +You can also update configuration parameter values when you use the [`build:project`][build] command. + +#### Examples + +To change values for the `MAGENTO_BASE_URL` and `BROWSER`: + +```bash +vendor/bin/mftf setup:env --MAGENTO_BASE_URL=http://magento.local/ --BROWSER=firefox +``` + +To create a `.env` file with example parameters: + +```bash +vendor/bin/mftf setup:env +``` + +The example parameters are taken from the `etc/config/.env.example` file. + +### `upgrade:tests` + +Applies all the MFTF major version upgrade scripts to test components in the given path (`test.xml`, `data.xml`, etc). + +#### Usage + +```bash +vendor/bin/mftf upgrade:tests +``` + +`` is the path that contains MFTF test components that need to be upgraded. +The command searches recursively for any `*.xml` files to upgrade. + +#### Examples + +To upgrade all test components inside modules in the `dev/tests/acceptance/tests/` directory: + +```bash +vendor/bin/mftf upgrade:tests /Users/user/magento2/dev/tests/acceptance/tests/ +``` + +To upgrade all test components inside the `Catalog` module: + +```bash +vendor/bin/mftf upgrade:tests /Users/user/magento2/app/code/Magento/Catalog/Test/Mftf/ +``` + + + +[configuration]: ../configuration.html +[Reference]: #reference +[build]: #buildproject +[setup]: #setupenv +[Reporting]: ../reporting.html + + + +*[MFTF]: Magento Functional Testing Framework diff --git a/docs/configuration.md b/docs/configuration.md new file mode 100644 index 000000000..f54498356 --- /dev/null +++ b/docs/configuration.md @@ -0,0 +1,265 @@ +--- +mftf-release: 2.3.7 +redirect_from: /guides/v2.3/magento-functional-testing-framework/2.3/configuration.html +--- + +# Configuration + +_This topic was updated due to the {{page.mftf-release}} MFTF release._ +{: style="text-align: right"} + +The `*.env` file provides additional configuration for the Magento Functional Testing Framework (MFTF). +To run the MFTF on your Magento testing instance, specify the basic configuration values. +Advanced users can create custom configurations based on requirements and environment. + +## Basic configuration + +These basic configuration values are __required__ and must be set by the user before the MFTF can function correctly. + +### MAGENTO_BASE_URL + +The root URL of the Magento application under test. + +Example: + +```conf +MAGENTO_BASE_URL=http://magento2.vagrant251 +``` + +{: .bs-callout .bs-callout-info } +If the `MAGENTO_BASE_URL` contains a subdirectory (like `http://magento.test/magento2ce`), specify [`MAGENTO_CLI_COMMAND_PATH`][]. + +### MAGENTO_BACKEND_NAME + +The path to the Magento Admin page. + +Example: + +```conf +MAGENTO_BACKEND_NAME=admin_12346 +``` + +### MAGENTO_ADMIN_USERNAME + +The username that tests can use to access the Magento Admin page + +Example: + +```conf +MAGENTO_ADMIN_USERNAME=admin +``` + +### MAGENTO_ADMIN_PASSWORD + +The password that tests will use to log in to the Magento Admin page. + +Example: + +```conf +MAGENTO_ADMIN_PASSWORD=1234reTyt%$7 +``` + +## Advanced configuration + +Depending on the environment you use, you may need to configure the MFTF more precisely by setting more configuration parameters then for basic configuration. +This section describes available configuration parameters and their default values (where applicable). + +### DEFAULT_TIMEZONE + +Sets a default value for the `timezone` attribute of a [`generateDate` action][generateDate]. +This value is applied when a test step does not specify a time zone. +For the complete list of available time zones, refer to [List of Supported Timezones][timezones]. + +Default: `America/Los_Angeles`. + +Example: + +```conf +DEFAULT_TIMEZONE=UTC +``` + +### SELENIUM + +The `SELENIUM_*` values form the URL of a custom Selenium server for running testing. + +Default Selenium URL: `http://127.0.0.1:4444/wd/hub` + +And the default configuration: + +```conf +SELENIUM_HOST=127.0.0.1 +SELENIUM_PORT=4444 +SELENIUM_PROTOCOL=http +SELENIUM_PATH=/wd/hub +``` + +{:.bs-callout .bs-callout-warning} +`SELENIUM_*` values are required if you are running Selenium on an external system. +If you change the configuration of the external Selenium server, you must update these values. + +#### SELENIUM_HOST + +Override the default Selenium server host. + +Example: + +```conf +SELENIUM_HOST=user:pass@ondemand.saucelabs.com +``` + +#### SELENIUM_PORT + +Override the default Selenium server port. + +Example: + +```conf +SELENIUM_PORT=443 +``` + +#### SELENIUM_PROTOCOL + +Override the default Selenium server protocol. + +Example: + +```conf +SELENIUM_PROTOCOL=https +``` + +#### SELENIUM_PATH + +Override the default Selenium server path. + +Example: + +```conf +SELENIUM_PATH=/wd/hub +``` + +### MAGENTO_RESTAPI + +These `MAGENTO_RESTAPI_*` values are optional and can be used in cases when your Magento instance has a different API path than the one in `MAGENTO_BASE_URL`. + +```conf +MAGENTO_RESTAPI_SERVER_HOST +MAGENTO_RESTAPI_SERVER_PORT +``` + +#### MAGENTO_RESTAPI_SERVER_HOST + +The protocol and the host of the REST API server path. + +Example: + +```conf +MAGENTO_RESTAPI_SERVER_HOST=http://localhost +``` + +#### MAGENTO_RESTAPI_SERVER_PORT + +The port part of the API path. + +Example: + +```conf +MAGENTO_RESTAPI_SERVER_PORT=5000 +``` + +### *_BP + +Settings to override base paths for the framework. +You can use it when the MFTF is applied as a separate tool. +For example, when you need to place the MFTF and the Magento codebase in separate projects. + +```conf +MAGENTO_BP +TESTS_BP +FW_BP +TESTS_MODULES_PATH +``` + +#### MAGENTO_BP + +The path to a local Magento codebase. +It enables the [`bin/mftf`][mftf] commands such as `run` and `generate` to parse all modules of the Magento codebase for MFTF tests. + +```conf +MAGENTO_BP=~/magento2/ +``` + +#### TESTS_BP + +BP is an acronym for _Base Path_. +The path to where MFTF supplementary files are located in the Magento codebase. + +Example: + +```conf +TESTS_BP=~/magento2ce/dev/tests/acceptance +``` + +#### FW_BP + +The path to MFTF. +FW_BP is an acronym for _FrameWork Base Path_. + +Example: + +```conf +FW_BP=~/magento/magento2-functional-testing-framework +``` + +#### TESTS_MODULE_PATH + +The path to where the MFTF modules mirror Magento modules. + +Example: + +```conf +TESTS_MODULE_PATH=~/magento2/dev/tests/acceptance/tests/functional/Magento/FunctionalTest +``` + +### MODULE_WHITELIST + +Use for a new module. +When adding a new directory at `Magento/FunctionalTest`, add the directory name to `MODULE_WHITELIST` to enable the MFTF to process it. + +Example: + +```conf +MODULE_WHITELIST=Magento_Framework,Magento_ConfigurableProductWishlist,Magento_ConfigurableProductCatalogSearch +``` + +### MAGENTO_CLI_COMMAND_PATH + +Path to the Magento CLI command entry point. + +Default: `dev/tests/acceptance/utils/command.php`. +It points to `MAGENTO_BASE_URL` + `dev/tests/acceptance/utils/command.php` + +Modify the default value: + +- for non-default Magento installation +- when use a subdirectory in the `MAGENTO_BASE_URL` + +Example: `dev/tests/acceptance/utils/command.php` + +### BROWSER + +Override the default browser performing the tests. + +Default: Chrome + +Example: + +```conf +BROWSER=firefox +``` + + + +[`MAGENTO_CLI_COMMAND_PATH`]: #magento_cli_command_path +[generateDate]: test/actions.html#generatedate +[mftf]: commands/mftf.html +[timezones]: http://php.net/manual/en/timezones.php \ No newline at end of file diff --git a/docs/credentials.md b/docs/credentials.md new file mode 100644 index 000000000..c5803c83b --- /dev/null +++ b/docs/credentials.md @@ -0,0 +1,101 @@ +--- +--- + +# Credentials + +When you test functionality that involves external services such as UPS, FedEx, PayPal, or SignifyD, use the MFTF credentials feature to hide sensitive [data][] like integration tokens and API keys. + +## Define sensitive data in `.credentials` + +The MFTF creates a sample file for credentials during [initial setup][]: `magento2/dev/tests/acceptance/.credentials.example`. +The file contains an example list of keys for fields that can require credentials. + +### Create `.credentials` + +To make the MFTF process the file with credentials, change directories to `magento2/dev/tests/acceptance/` and copy `.credentials.example` to `.credentials`. + +```bash +cd dev/tests/acceptance/ +``` + +```bash +cp .credentials.example .credentials +``` + +### Add `.credentials` to `.gitignore` + +Verify that the file is excluded from tracking by `.gitignore` (unless you need this behavior): + +```bash +git check-ignore .credentials +``` + +The command outputs the path if the file is excluded: + +```terminal +.credentials +``` + +### Define sensitive data + +Open the `.credentials` file, uncomment the fields you want to use, and add your values: + +```config +... +# Credentials for the USPS service +carriers_usps_userid=test_user +carriers_usps_password=Lmgxvrq89uPwECeV + +# Credentials for the DHL service +#carriers/dhl/id_us= +#carriers/dhl/password_us= +.... +``` + +{: .bs-callout .bs-callout-info } +The `/` symbol is not supported in a key name. + +You are free to use any other keys you like, as they are merely the keys to reference from your tests. + +```conf +# Credentials for the MyAwesome service +my_awesome_service_token=rRVSVnh3cbDsVG39oTMz4A + +# Credentials for the USPS service +carriers_usps_userid=test_user +carriers_usps_password=Lmgxvrq89uPwECeV +.... +``` + +## Use credentials in a test + + +Access the data defined in the `.credentials` file using the [`fillField`][] action with the `userInput` attribute. +Define the value as a reference to the corresponding key in the credentials file such as `{{_CREDS.my_data_key}}`: + +- `_CREDS` is an environment constant pointing to the `.credentials` file +- `my_data_key` is a key in the the `.credentials` file that contains the value to be used in a test step + +For example: + +```xml + +``` + +## Implementation details + +The generated tests do not contain credentials values. +The MFTF dynamically retrieves, encrypts, and decrypts the sensitive data during test execution. +Decrypted credentials do not appear in the console, error logs, or [test reports][]. +The decrypted values are only available in the `.credentials` file. + +{: .bs-callout .bs-callout-info } +The MFTF tests delivered with Magento application do not use credentials and do not cover external services, because of sensitivity of the data. + + + + +[`fillField`]: test/actions.html#fillfield +[data]: data.html +[initial setup]: getting-started.html +[test reports]: reporting.html diff --git a/docs/data.md b/docs/data.md new file mode 100644 index 000000000..c3c588275 --- /dev/null +++ b/docs/data.md @@ -0,0 +1,266 @@ +--- +mftf-release: 2.3.0 +redirect_from: /guides/v2.3/magento-functional-testing-framework/2.3/data.html +--- + +# Input testing data + +_This topic was updated due to the {{page.mftf-release}} MFTF release._ +{: style="text-align: right"} + +The MFTF enables you to specify and use `` entities defined in XML. Default `` entities are provided for use and as templates for entity creation and manipulation. +The following diagram shows the XML structure of an MFTF data object: + +{%include_relative img/data-dia.svg%} + +## Supply data to test by reference to a data entity + +{%raw%} +Test steps requiring `` input in an action, like filling a field with a string, may reference an attribute from a data entity: + +```xml +userInput="{{SimpleSubCategory.name}}" +``` + +In this example: + +* `SimpleSubCategory` is an entity name. +* `name` is a `` key of the entity. The corresponding value will be assigned to `userInput` as a result. + +### Environmental data + +```xml +userInput="{{_ENV.MAGENTO_ADMIN_USERNAME}}" +``` + +In this example: + +* `_ENV` is a reference to the `dev/tests/acceptance/.env` file, where basic environment variables are set. +* `MAGENTO_ADMIN_USERNAME` is a name of an environment variable. + The corresponding value will be assigned to `userInput` as a result. + +### Sensitive data + +```xml +userInput="{{_CREDS.my_secret_token}}" +``` + +In this example: + +* `_CREDS` is a constant to reference to the `dev/tests/acceptance/.credentials` file, where sensitive data and secrets are stored for use in a test. +* `MY_SECRET_TOKEN` is the name of a key in the credentials variable. + The corresponding value of the credential will be assigned to `userInput` as a result. +* The decrypted values are only available in the `.credentials` file in which they are stored. + +Learn more in [Credentials][]. + +## Persist a data entity as a prerequisite of a test {#persist-data} + +A test can specify an entity to be persisted (created in the database) so that the test actions could operate on the existing known data. + +Example of referencing `data` in a test: + +```xml +userInput="$createCustomer.email$" +``` + +{%endraw%} +In this example: + +* `createCustomer` is a step key of the corresponding test step that creates an entity. +* `email` is a data key of the entity. + The corresponding value will be assigned to `userInput` as a result. + +{% +include note.html +type="info" +content='As of MFTF 2.3.6, you no longer need to differentiate between scopes (a test, a hook, or a suite) for persisted data when referencing it in tests. + +The MFTF now stores the persisted data and attempts to retrieve it using the combination of `stepKey` and the scope of where it has been called. +The current scope is preferred, then widening to _test > hook > suite_ or _hook > test > suite_. + +This emphasizes the practice for the `stepKey` of `createData` to be descriptive and unique, as a duplicated `stepKey` in both a `` and `` prefers the `` data.' +%} + +## Use data returned by test actions + +{%raw%} +A test can also reference data that was returned as a result of [test actions](./test/actions.html#actions-returning-a-variable), like the action ` + +``` + +## Hard-coded data input + +The data to operate against can be included as literals in a test. Hard-coded data input can be useful in assertions. + +See also [Actions](./test/actions.html). + +```xml +userInput="We'll email you an order confirmation with details and tracking info." +``` + +## Format + +The format of `` is: + +```xml + + + + + + + + + + + +``` + +## Principles + +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. + +## Example + +Example (`.../Catalog/Data/CategoryData.xml` file): + +```xml + + + + + simpleCategory + simplecategory + true + + + SimpleSubCategory + simplesubcategory + true + true + + +``` + +This example declares two `` entities: `_defaultCategory` and `SimpleSubCategory`. They set the data required for [category creation](http://docs.magento.com/m2/ce/user_guide/catalog/category-create.html). + +All entities that have the same name will be merged during test generation. Both entities are of the `category` type. + +`_defaultCategory` sets three data fields: + +* `name` defines the category name as `simpleCategory` with a unique suffix. Example: `simpleCategory598742365`. +* `name_lwr` defines the category name in lowercase format with a unique suffix. Example: `simplecategory697543215`. +* `is_active` sets the enable category to `true`. + +`SimpleSubCategory` sets four data fields: + +* `name` that defines the category name with a unique suffix. Example: `SimpleSubCategory458712365`. +* `name_lwr` that defines the category name in lowercase format with a unique suffix. Example: `simplesubcategory753698741`. +* `is_active` sets the enable category to `true`. +* `include_in_menu` that sets the include in the menu to `true`. + +The following is an example of a call in test: + +```xml + +``` + +This action inputs data from the `name` of the `_defaultCategory` entity (for example, `simpleCategory598742365`) into the field with the locator defined in the selector of the `categoryNameInput` element of the `AdminCategoryBasicFieldSection`. + +## Reference + +### entities {#entities-tag} + +`` is an element that contains all `` elements. + +### entity {#entity-tag} + +`` is an element that contains `` elements. + +Attributes|Type|Use|Description +---|---|---|--- +`name`|string|optional|Name of the ``. +`type`|string|optional|Node containing the exact name of `` type. Used later to find specific Persistence Layer Model class. `type` in `` can be whatever the user wants; There are no constraints. It is important when persisting data, depending on the `type` given, as it will try to match a metadata definition with the operation being done. Example: A `myCustomer` entity with `type="customer"`, calling ``, will try to find a metadata entry with the following attributes: ``. + +`` may contain one or more [``](#data-tag), [``](#var-tag), [``](#requiredentity-tag), or [``](#array-tag) elements in any sequence. + +### data {#data-tag} + +`` is an element containing a data/value pair. + +Attributes|Type|Use|Description +---|---|---|--- +`key`|string|optional|Key attribute of data/value pair. +`unique`|enum: `"prefix"`, `"suffix"`|optional|Add suite or test wide unique sequence as "prefix" or "suffix" to the data value if specified. + +### var {#var-tag} + +`` is an element that can be used to grab a key value from another entity. For example, when creating a customer with the `` action, the server responds with the auto-incremented ID of that customer. Use `` to access that ID and use it in another data entity. + +Attributes|Type|Use|Description +---|---|---|--- +`key`|string|optional|Key attribute of this entity to assign a value to. +`entityType`|string|optional|Type attribute of referenced entity. +`entityKey`|string|optional|Key attribute of the referenced entity from which to get a value. +`unique`|--|--|*This attribute hasn't been implemented yet.* + +### requiredEntity {#requiredentity-tag} + +`` is an element that specifies the parent/child relationship between complex types. + +Example: You have customer address info. To specify that relationship: + +```xml + + ... + AddressEntity + ... + +``` + +Attributes|Type|Use|Description +---|---|---|--- +`type`|string|optional|Type attribute of ``. + +### array {#array-tag} + +`` is an element that contains a reference to an array of values. + +Example: + +```xml + + ... + + 7700 W Parmer Ln + Bld D + + ... + +``` + +Attributes|Type|Use|Description +---|---|---|--- +`key`|string|required|Key attribute of this entity in which to assign a value. + +`` may contain [``](#item-tag) elements. + +### item {#item-tag} + +`` is an individual piece of data to be passed in as part of the parent `` type. + +{%endraw%} diff --git a/docs/debugging.md b/docs/debugging.md new file mode 100644 index 000000000..f057756bf --- /dev/null +++ b/docs/debugging.md @@ -0,0 +1,40 @@ +--- +title: Debugging MFTF Tests +mftf-release: 2.3.13 +--- + +Debugging within the Magento Functional Testing Framework is helpful in identifying test bugs by allowing you to pause execution so that you may: + +- Examine the page. +- Check returned data and other variables being used during run-time. + +This is straightforward to do once you create a basic Debug Configuration. + +## Prerequisites + +- [Xdebug][] +- PHPUnit configured for use in [PHPStorm][] + +## Creating Debug Configuration with PHPStorm + +1. If not already installed, download the Codeception Framework plugin for PHPStorm (`PhpStorm->Preferences->Plugins`). +1. Click `Edit Configurations` on the configuration dropdown. +1. Click `+` and select `Codeception` from the available types. +1. Change `Test Scope` to `Type` and select `functional` from the `Type:` dropdown. +1. Find the `Custom Working Directory` option and set the path to your `dev/tests/acceptance/` directory. + +If you get a warning `Path to Codeception for local machine is not configured.`: + +1. Click `Fix`, then `+`, and select `Codeception Local`. +1. Click `...` and locate `/vendor/bin/codecept` in your Magento installation folder. + +The easiest method of tagging a test for debugging is the following: + +- In your Debug configuration, locate `Test Runner options:` and set `--group testDebug`. +- When you want to debug a test you are working on, simply add `` to the annotations. Be sure to remove this after done debugging. + +Your Debug Configuration should now be able to run your test and pause execution on any breakpoints you have set in the generated `.php` file under the `_generated` folder. + + +[Xdebug]: https://xdebug.org/docs/install +[PHPStorm]: https://github.com/SeleniumHQ/selenium/wiki/PageObjects diff --git a/docs/extending.md b/docs/extending.md new file mode 100644 index 000000000..7bd36c31c --- /dev/null +++ b/docs/extending.md @@ -0,0 +1,372 @@ +--- +mftf-release: 2.3.0 +redirect_from: /guides/v2.3/magento-functional-testing-framework/2.3/extending.html +--- + +# Extending + +_This topic was updated due to the {{page.mftf-release}} MFTF release._ +{: style="text-align: right"} + +There are cases when you need to create many tests that are very similar to each other. +For example, only one or two parameters (for example, URL) might vary between tests. +To avoid copy-pasting and to save some time the Magento Functional Testing Framework (MFTF) enables you to extend test components such as [test], [data], and [action group]. +You can create or update any component of the parent body in your new test/action group/entity. + +* A test starting with `` creates a test `SampleTest` that takes body of existing test `ParentTest` and adds to it the body of `SampleTest`. +* An action group starting with `` creates an action group based on the `ParentActionGroup`, but with the changes specified in `SampleActionGroup`. +* An entity starting with `` creates an entity `SampleEntity` that is equivalent to merging the `SampleEntity` with the `ParentEntity`. + +Specify needed variations for a parent object and produce a copy of the original that incorporates the specified changes (the "delta"). + +Unlike merging, the parent test (or action group) will still exist after the test generation. + {:.bs-callout .bs-callout-info} + +## Extending tests + +### Update a test step + +__Use case__: Create two similar tests with different `url` (`"{{AdminCategoryPage.url}}"` and `"{{OtherCategoryPage.url}}"`) in a test step. + +> Test with "extends": + +```xml + + + + ... + + ...(several steps) + + ...(several steps) + + + + ... + + + + +``` + +> Test without "extends": + +```xml + + + + ... + + ...(several steps) + + ...(several steps) + + + + ... + + ...(several steps) + + ...(several steps) + + +``` + +### Add a test step + +__Use case__: Create two similar tests where the second test contains two additional steps: + +* `checkOption` before `click` (`stepKey="clickLogin"`) +* `seeInCurrentUrl` after `click` in the `LogInAsAdminTest` test (in the `.../Backend/Test/LogInAsAdminTest.xml` file) + +> Tests with "extends": + +```xml + + + + + + + + + + + + + +``` + +> Tests without "extends": + +```xml + + + + + + + + + + + + + + + + + + +``` + +### Update a test annotation + +__Use case__: Create two similar tests where the second one contains two additional actions in the `before` hook: + +* `checkOption` before `click` (`stepKey="clickLogin"`) +* `seeInCurrentUrl` after `click` in the `LogInAsAdminTest` test (in the `.../Backend/Test/LogInAsAdminTest.xml` file) + +> Tests with "extends": + +```xml + + + + + + + + + + + + + + + + + +``` + +> Tests without "extends": + +```xml + + + + + + + + + + + + + + + + + + + + + + +``` + +## Extending action groups + +Extend an [action group] to add or update [actions] in your module. + +### Update an action + +__Use case__: The `CountProductA` test counts the particular product. +Modify the action group to use another product. + +> Action groups with "extends": + +```xml + + + + + + + + {{count}} + grabProducts + + + + + + + +``` + +> Action groups without "extends": + +```xml + + + + + + + + {{count}} + grabProducts + + + + + + + + + + {{count}} + grabProducts + + + +``` + +### Add an action + +__Use case__: The `GetProductCount` action group returns the count of products. +Add a new test `VerifyProductCount` that asserts the count of products: + +> Action groups with "extends": + +```xml + + + + + + + + + + + + + + {{count}} + grabProducts + + + +``` + +> Action groups without "extends": + +```xml + + + + + + + + + + + + + + + + {{count}} + grabProducts + + + +``` + +## Extending data + +Extend data to reuse entities in your module. + +### Update a data entry + +__Use case__: Create an entity named `DivPanelGreen`, which is similar to the `DivPanel` entity, except that it is green. + +> Entities with "extends": + +```xml + + + Red + 80px + 100% + + + Green + + +``` + +> Entities without "extends": + +```xml + + + Red + 80px + 100% + + + Green + 80px + 100% + + +``` + +### Add a data entry + +__Use case__: Create an entity named `DivPanelGreen`, which is similar to the `DivPanel` entity, except that it has a specific panel color. + +> Entities with "extends": + +```xml + + + Red + 80px + 100% + + + #000000 + True + + +``` + +> Entities without "extends": + +```xml + + + Red + 80px + 100% + + + #000000 + 80px + 100% + True + + +``` + + +[test]: ./test.html +[data]: ./data.html +[action group]: ./test/action-groups.html +[actions]: ./test/actions.html \ No newline at end of file diff --git a/docs/getting-started.md b/docs/getting-started.md new file mode 100644 index 000000000..b49ab76d2 --- /dev/null +++ b/docs/getting-started.md @@ -0,0 +1,311 @@ +--- +mftf-release: 2.3.8 +redirect_from: /guides/v2.3/magento-functional-testing-framework/2.3/getting-started.html +--- + +# Getting started + +_This topic was updated after {{page.mftf-release}} MFTF release._ +{: style="text-align: right"} + +{% include_relative include/note-2.2-docs.md %} + +## Prepare environment {#prepare-environment} + +Make sure that you have the following software installed and configured on your development environment: + +- [PHP version supported by the Magento instance under test][php] +- [Composer 1.3 or later][composer] +- [Java 1.8 or later][java] +- [Selenium Server Standalone 3.6 or later][selenium server] and [ChromeDriver 2.33 or later][chrome driver] or other webdriver in the same directory + +{:.bs-callout .bs-callout-tip} +[PhpStorm] supports [Codeception test execution][], which is helpful when debugging. + +## Install Magento {#install-magento} + +Use instructions below to install Magento. + +### Step 1. Clone the `magento2` source code repository {#clone-magento} + +```bash +git clone https://github.com/magento/magento2.git +``` + +or + +```bash +git clone git@github.com:magento/magento2.git +``` + +### Step 2. Install dependencies {#install-dependencies} + +Checkout the Magento version that you are going to test. + +```bash +cd magento2/ +``` + +```bash +git checkout 2.3-develop +``` + +Install the Magento application. + +```bash +composer install +``` + +## Prepare Magento {#prepare-magento} + +Configure the following settings in Magento as described below. + +### WYSIWYG settings {#wysiwyg-settings} + +A Selenium web driver cannot enter data to fields with {% glossarytooltip 98cf4fd5-59b6-4610-9c1f-b84c8c0abd97 %}WYSIWYG{% endglossarytooltip %}. + +To disable the WYSIWYG and enable the web driver to process these fields as simple text areas: + +1. Log in to the {% glossarytooltip 18b930cf-09cc-47c9-a5e5-905f86c43f81 %}Magento Admin{% endglossarytooltip %} as an administrator. +2. Navigate to **Stores \> Configuration \> General \> Content Management**. +3. In the WYSIWYG Options section set the **Enable WYSIWYG Editor** option to **Disabled Completely**. +4. Click **Save Config**. + +{: .bs-callout .bs-callout-tip } +When you want to test the WYSIWYG functionality, re-enable WYSIWYG in your test suite. + +### Security settings {#security-settings} + +To enable the **Admin Account Sharing** setting, to avoid unpredictable logout during a testing session, and disable the **Add Secret Key in URLs** setting, to open pages using direct URLs: + +1. Navigate to **Stores \> Configuration \> Advanced \> {% glossarytooltip 29ddb393-ca22-4df9-a8d4-0024d75739b1 %}Admin{% endglossarytooltip %} \> Security**. +2. Set **Admin Account Sharing** to **Yes**. +3. Set **Add Secret Key to URLs** to **No**. +4. Click **Save Config**. + +## 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. +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][]. + +Install the MFTF. + +```bash +composer install +``` + +### Step 1. Build the project {#build-project} + +In the Magento project root, run: + +```bash +vendor/bin/mftf build:project +``` + +{% include note.html +type='tip' +content='If you use PhpStorm, generate a URN catalog: + +```bash +vendor/bin/mftf generate:urn-catalog .idea/ +``` + +If the file does not exist, add the `--force` option to create it: + +```bash +vendor/bin/mftf generate:urn-catalog --force .idea/ +``` + +See [`generate:urn-catalog`][] for more details.' +%} + +{:.bs-callout .bs-callout-tip} +You can simplify command entry by adding the absolute path to the `vendor/bin` directory path to your PATH environment variable. +After adding the path, you can run `mftf` without having to include `vendor/bin`. + +### Step 2. Edit environmental settings {#environment-settings} + +In the `magento2/dev/tests/acceptance/` directory, edit the `.env` file to match your system. + +```bash +vim dev/tests/acceptance/.env +``` + +Specify the following parameters, which are required to launch tests: + +- `MAGENTO_BASE_URL` must contain a domain name of the Magento instance that will be tested. + Example: `MAGENTO_BASE_URL=http://magento.test` + +- `MAGENTO_BACKEND_NAME` must contain the relative path for the Admin area. + Example: `MAGENTO_BACKEND_NAME=admin` + +- `MAGENTO_ADMIN_USERNAME` must contain the username required for authorization in the Admin area. + Example: `MAGENTO_ADMIN_USERNAME=admin` + +- `MAGENTO_ADMIN_PASSWORD` must contain the user password required for authorization in the Admin area. + Example: `MAGENTO_ADMIN_PASSWORD=123123q` + +{: .bs-callout .bs-callout-info } +If the `MAGENTO_BASE_URL` contains a subdirectory like `http://magento.test/magento2ce`, specify `MAGENTO_CLI_COMMAND_PATH`. + +Learn more about environmental settings in [Configuration][]. + +### Step 3. Enable the Magento CLI commands + +In the `magento2/dev/tests/acceptance` directory, run the following command to enable the MFTF to send Magento CLI commands to your Magento instance. + + ```bash +cp dev/tests/acceptance/.htaccess.sample dev/tests/acceptance/.htaccess +``` + +### Step 4. Generate and run tests {#run-tests} + +To run tests, you need a running Selenium server and [`mftf`][] commands. + +#### Run the Selenium server {#selenium-server} + +Run the Selenium server in terminal. +For example, the following commands run the Selenium server for Google Chrome: + +```bash +cd / +``` + +```bash +java -Dwebdriver.chrome.driver=chromedriver -jar selenium-server-standalone-3.14.0.jar +``` + +#### Generate and run all tests {#run-all-tests} + +```bash +vendor/bin/mftf generate:tests +``` + +```bash +cd dev/tests/acceptance +``` + +```bash +vendor/bin/codecept run functional +``` + +See more commands in [`codecept`][]. + +#### Run a simple test {#run-test} + +To clean up the previously generated tests, and then generate and run a single test `AdminLoginTest`, run: + +```bash +vendor/bin/mftf run:test AdminLoginTest --remove +``` + +See more commands in [`mftf`][]. + +### Step 5. Generate reports {#reports} + +During testing, the MFTF generates test reports in CLI. +You can generate visual representations of the report data using [Allure Framework][]. +To view the reports in GUI: + +- [Install Allure][] +- Run the tool to serve the artifacts in `dev/tests/acceptance/tests/_output/allure-results/`: + +```bash +allure serve dev/tests/acceptance/tests/_output/allure-results/ +``` + +Learn more about Allure in the [official documentation][allure docs]. + +## Set up a standalone MFTF + +The MFTF is a root level Magento dependency, but it is also available for use as a standalone application. +You may want to use a standalone application when you develop for or contribute to MFTF, which facilitates debugging and tracking changes. +These guidelines demonstrate how to set up and run Magento acceptance functional tests using standalone MFTF. + +### Prerequisites + +This installation requires a local instance of the Magento application. +The MFTF uses the [tests from Magento modules][mftf tests] as well as the `app/autoload.php` file. + +### Step 1. Clone the MFTF repository + +If you develop or contribute to the MFTF, it makes sense to clone your fork of the MFTF repository. +For contribution guidelines, refer to the [Contribution Guidelines for the Magento Functional Testing Framework][contributing]. + +### Step 2. Install the MFTF + +```bash +cd magento2-functional-testing-framework +``` + +```bash +composer install +``` + +### Step 3. Build the project {#build-project} + +```bash +bin/mftf build:project +``` + +### Step 4. Edit environment settings + +In the `dev/.env` file, define the [basic configuration][] and [`MAGENTO_BP`][] parameters. + +### Step 5. Enable the Magento CLI commands {#add-cli-commands} + +Copy the `etc/config/command.php` file into your Magento installation at `/dev/tests/acceptance/utils/`. +Create the `utils/` directory, if you didn't find it. + +### Step 6. Remove the MFTF package dependency in Magento + +The MFTF uses the Magento `app/autoload.php` file to read Magento modules. +The MFTF dependency in Magento supersedes the standalone registered namespaces unless it is removed at a Composer level. + +```bash +composer remove magento/magento2-functional-testing-framework --dev -d +``` + +### Step 7. Run a simple test + +Generate and run a single test that will check your logging to the Magento Admin functionality: + +```bash +bin/mftf run:test AdminLoginTest +``` + +You can find the generated test at `dev/tests/functional/tests/MFTF/_generated/default/`. + +### Step 8. Generate Allure reports + +The standalone MFTF generates Allure reports at `dev/tests/_output/allure-results/`. +Run the Allure server pointing to this directory: + +```bash +allure serve dev/tests/_output/allure-results/ +``` + + + +[`codecept`]: commands/codeception.html +[`generate:urn-catalog`]: commands/mftf.html#generateurn-catalog +[`MAGENTO_BP`]: configuration.html#magento_bp +[`mftf`]: commands/mftf.html +[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/ +[Configuration]: configuration.html +[contributing]: https://github.com/magento/magento2-functional-testing-framework/blob/develop/.github/CONTRIBUTING.md +[install Allure]: https://github.com/allure-framework/allure2#download +[java]: http://www.oracle.com/technetwork/java/javase/downloads/index.html +[mftf tests]: introduction.html#mftf-tests +[php]: {{ site.gdeurl23 }}install-gde/system-requirements-tech.html#php +[PhpStorm]: https://www.jetbrains.com/phpstorm/ +[selenium server]: https://www.seleniumhq.org/download/ +[Set up a standalone MFTF]: #set-up-a-standalone-mftf +[test suite]: suite.html diff --git a/docs/img/catalogCategoryRepository-operations.png b/docs/img/catalogCategoryRepository-operations.png new file mode 100644 index 0000000000000000000000000000000000000000..71a5bf7c283f9d4db8a29f70037dc953b7f29cb3 GIT binary patch literal 20478 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 + + + + + image/svg+xml + + + + + + + 0..∞ + + + + + data + + + 0..∞ + + + + + var + + + 0..∞ + + + + + requiredEntity + + + 0..∞ + + + + + item + + + + + + + + + + + + + + 0..∞ + + + + + array + + + + + + + + + + + + + + + + + + + + + + + 1..∞ + + + + + + + + + entity + + + 0..∞ + + + + + + + + + + + + + + + entities + + + diff --git a/docs/img/issue.png b/docs/img/issue.png new file mode 100644 index 0000000000000000000000000000000000000000..5f9a4e297b2d0047391d929034e3b6eb0e2b762f GIT binary patch literal 18617 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 literal 0 HcmV?d00001 diff --git a/docs/img/metadata-dia.svg b/docs/img/metadata-dia.svg new file mode 100644 index 000000000..0ce0fc27a --- /dev/null +++ b/docs/img/metadata-dia.svg @@ -0,0 +1,1050 @@ + + + + + + image/svg+xml + + + + + + + + + + + field + 0..∞ + + + + + + + array + 0..∞ + + + + + + + + + + + + object + 0..∞ + + + + + + + + + + + + + + + + + + + + + + 0..∞ + + + + + + + + + + field + 0..∞ + + + + + + value + + + + + + + object + 0..∞ + + + + + + + + + + + + + + + + + + + + + + + 0..∞ + + + + + + + + + + array + 0..∞ + + + + + + + + + header + + + 0..∞ + + + + + param + 0..∞ + + + + + + + + object + 0..∞ + + + + + + + + + + + + + + contentType + + + + + + + + + + + + + + + + + + + 0..∞ + + + + + + + + + + operation + 0..∞ + + + + + + + + + + + + + + + + + + + + + operations + + + + + diff --git a/docs/img/mftf-fork.gif b/docs/img/mftf-fork.gif new file mode 100644 index 0000000000000000000000000000000000000000..650687b06796ca0a8f220794635ffa65cbabe039 GIT binary patch literal 420980 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ΞGlp<0Z|-G?3rYw{ZI?_`m1sWf5V=bzb|JvS;%E? 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 ILP+!f3b!)8>Hq)$ literal 0 HcmV?d00001 diff --git a/docs/img/page-dia.svg b/docs/img/page-dia.svg new file mode 100644 index 000000000..668891db0 --- /dev/null +++ b/docs/img/page-dia.svg @@ -0,0 +1,290 @@ + + + + + + image/svg+xml + + + + + + + + + + section + + 0..∞ + + + + + + + + + + + + + + + + + page + + 1..∞ + + + + + + + + + + + + + + + + + + + pages + + + + + diff --git a/docs/img/pull-request.png b/docs/img/pull-request.png new file mode 100644 index 0000000000000000000000000000000000000000..5ed25156f8a892095fca937afe6f76f86492f9b2 GIT binary patch literal 10372 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 SIfH+XxP9a9^^$8(U;Qs?YKi~= literal 0 HcmV?d00001 diff --git a/docs/img/section-dia.svg b/docs/img/section-dia.svg new file mode 100644 index 000000000..1ee7611af --- /dev/null +++ b/docs/img/section-dia.svg @@ -0,0 +1,296 @@ + + + + + + image/svg+xml + + + + + + + + + + + element + + + 1..∞ + + + + + + + + + + + + + + + + + + + + + 1..∞ + + + + + + + + + + + + + + + + + + + + + sections + + + + + diff --git a/docs/img/switching-the-base.png b/docs/img/switching-the-base.png new file mode 100644 index 0000000000000000000000000000000000000000..30aa430719b6152fb55e1a51ec9b39d0b4c5b293 GIT binary patch literal 9796 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 aMgBcpi)a?0*2 + + + + + image/svg+xml + + + + + + + + + testTypeTags + + + + + + + + + testTypeTags + + + + + + + + + + + + + + + + + + + + + 0..∞ + + + + + + + + + before + + + + + + + testTypeTags + + + + + + + + + + + + + + + + + + + + + 0..∞ + + + + + + + + + after + + + + + + + + annotations + + + + + + + + + + + + + + + + + + + + + + + + + 0..∞ + + + + + + + + + + test + 1..∞ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + tests + + + + + + + + actionTypeTags + + + + + + + + + + + argument + + + + + + + + + + + + 0..∞ + + + + + + + + + actionGroup + + + + + + + + + + + + + + + + + + + + + + + + + + + testTypeTags + + + diff --git a/docs/img/trouble-chrome232.png b/docs/img/trouble-chrome232.png new file mode 100644 index 0000000000000000000000000000000000000000..99eac179aaea70e6a28ed6d05ebef3d51d118983 GIT binary patch literal 49401 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 X{Cf7#u^nIg0P{^$_G{&5UH|_FidvzD literal 0 HcmV?d00001 diff --git a/docs/include/note-2.2-docs.md b/docs/include/note-2.2-docs.md new file mode 100644 index 000000000..ec41ef9ae --- /dev/null +++ b/docs/include/note-2.2-docs.md @@ -0,0 +1,19 @@ +
+[Find out your version]({{site.baseurl}}/mftf/2.3/introduction.html#find-version) of the MFTF. +If your version is 2.2, see the [MFTF 2.2 documentation]({{site.baseurl}}/mftf/2.2/introduction.html). + +{% assign packages_2_3 = site.data.codebase.v2_3.open-source.composer_lock.packages-dev %} +{% assign packages_2_2 = site.data.codebase.v2_2.open-source.composer_lock.packages-dev %} + +{% for package in packages_2_3 %} +{% if package.name contains 'magento2-functional-testing-framework' %} +The latest Magento 2.2 release supports MFTF {{ package.version }}. +{% endif %} +{% endfor %} + +{% for package in packages_2_2 %} +{% if package.name contains 'magento2-functional-testing-framework' %} +The latest Magento 2.3 release supports MFTF {{ package.version }}. +{% endif %} +{% endfor %} +
\ No newline at end of file diff --git a/docs/introduction.md b/docs/introduction.md new file mode 100644 index 000000000..524665a14 --- /dev/null +++ b/docs/introduction.md @@ -0,0 +1,141 @@ +--- +mftf-release: 2.3.14 +redirect_from: /guides/v2.3/magento-functional-testing-framework/2.3/introduction.html +--- + +# Introduction to the Magento Functional Testing Framework version 2.3 + +_The latest MFTF release is [{{page.mftf-release}}]._ +{: style="text-align: right"} + +{% include_relative include/note-2.2-docs.md %} + +The Magento Functional Testing Framework (MFTF) aims to replace the [Functional Testing Framework] in future releases. +MFTF improves: + +- **Traceability** for clear logging and reporting capabilities. +- **Modularity** to run tests based on installed modules and extensions. +- **Customizability** for existing tests. +- **Readability** using clear and declarative XML test steps. +- **Maintainability** based on simple test creation and overall structure. + +Because MFTF tests are written in XML, you no longer need to learn PHP to write tests. + +{:.bs-callout .bs-callout-info} +We are actively developing functional tests. +Refer to `/app/code///Test/Mftf/` for examples. + +## Audience + +This MFTF guide is intended for Magento developers and software engineers, such as QA specialists, PHP developers, and system integrators. + +## Goals + +The purpose of MFTF is to: + +- Facilitate functional testing and minimize the effort it takes to perform regression testing. +- Make it easier to support the extension and customization of tests via XML merging. + +## Scope + +MFTF will enable you to: + +- Test user interactions with web applications in testing. +- Write functional tests located in `/app/code///Test/Mftf/`. +- Cover basic functionality using out-of-the-box tests. You can test extended functionality using custom tests. +- Automate regression testing. + +## Use cases + +As a Magento developer, test changes, such as extended search functionality, a new form attribute, or new product tags. + +As a software engineer, perform regression testing before release to ensure that Magento works as expected with new functionality. + +## Find your MFTF version {#find-version} + +There are two options to find out your MFTF version: + +- using the MFTF CLI +- using the Composer CLI + +### MFTF CLI + +```bash +cd / +``` + +```bash +vendor/bin/mftf --version +``` + +### Composer CLI + +```bash +cd / +``` + +```bash +composer show magento/magento2-functional-testing-framework +``` + +## Contents of dev/tests/acceptance + +```tree +tests + _data // Additional files required for tests (e.g. pictures, CSV files for import/export, etc.) + _output // The directory is generated during test run. It contains testing reports. + _suite // Test suites. + _bootstrap.php // The script that executes essential initialization routines. + functional.suite.dist.yml // The Codeception functional test suite configuration (generated while running 'bin/mftf build:project') +utils // The test-running utilities. +.env.example // Example file for environmental settings. +.credentials.example // Example file for credentials to be used by the third party integrations (generated while running 'bin/mftf build:project'; should be filled with the appropriate credentials in the corresponding sandboxes). +.gitignore // List of files ignored by git. +.htaccess.sample // Access settings for the Apache web server to perform the Magento CLI commands. +codeception.dist.yml // Codeception configuration (generated while running 'bin/mftf build:project') +``` + +## MFTF output + +- Generated PHP Codeception tests +- Codeception results and console logs +- Screenshots and HTML failure report +- Allure formatted XML results +- Allure report dashboard of results + +## MFTF tests + +The MFTF supports two different locations for storing the tests and test artifacts: + +- `/app/code///Test/Mftf/` is the directory to create new tests. +- `/vendor///Test/Mftf/` is the directory with the out of the box tests (fetched by the Composer). + +All tests and test data from these locations are merged in the order indicated in the above list. + +The file structure under the both path cases is the same: + +```tree + +├── ActionGroup +│   └── ... +├── Data +│   └── ... +├── Metadata +│   └── ... +├── Page +│   └── ... +├── Section +│   └── ... +└── Test + └── ... +``` + +## MFTF on Github + +Follow the [MFTF project] and [contribute on Github]. + + +[contribute on Github]: https://github.com/magento/magento2-functional-testing-framework/blob/master/.github/CONTRIBUTING.md +[Functional Testing Framework]: {{ site.gdeurl23 }}mtf/mtf_introduction.html +[MFTF project]: https://github.com/magento/magento2-functional-testing-framework +[{{page.mftf-release}}]: https://github.com/magento/magento2-functional-testing-framework/releases/tag/{{page.mftf-release}} diff --git a/docs/merging.md b/docs/merging.md new file mode 100644 index 000000000..ac4488aeb --- /dev/null +++ b/docs/merging.md @@ -0,0 +1,575 @@ +--- +mftf-release: 2.3.0 +redirect_from: /guides/v2.3/magento-functional-testing-framework/2.3/merging.html +--- + +# Merging + +_This topic was updated due to the {{page.mftf-release}} MFTF release._ +{: style="text-align: right"} + +The MFTF allows you to merge test components defined in XML files, such as: + +- [``][] +- [``][] +- [``][] +- [``][] +- `` + +You can create, delete, or update the component. +It is useful for supporting rapid test creation for extensions and customizations. + +You can specify needed changes to an existing file and merge them to produce a modification of the original that incorporates the specified changes (the "delta"). + +Merging operates at the XML tag level, triggered by our parser when there are two (or more) entities with the same name. +Your update (XML node with changes) must have the same attribute `name` as its base node (the target object to be changed). + +For example: + +- All tests with `` will be merged into one. +- All sections with `
` will be merged into one. +- All data entities with `` will be merged into one. +- All action groups with `` will be merged into one. + +Although a file name does not influence merging, we recommend using the same file names in merging updates. +This makes it easier to search later on. + +## Add a test + +You cannot add another [``][tests] using merging functionality. +To add a ``, create a new `*Test.xml` file or add a `` node to an existing `*Test.xml` file. + +## Remove a test + +Tests cannot be removed while merging. +If a [``][tests] must be skipped due to a module completely invalidating a function, you can add the test to the `skip` group. + +Learn more about running tests with different options using [`mftf`] or [`codecept`] commands. + +### Example + +Skip the `AdminLoginTest` test in the `.../Backend/Test/AdminLoginTest.xml` file while merging with the `.../Foo/Test/AdminLoginTest.xml` file: + +```xml + + + + + + + <description value="You should be able to log into the Magento Admin backend."/> + <severity value="CRITICAL"/> + <testCaseId value="MAGETWO-71572"/> + <group value="example"/> + <group value="login"/> + </annotations> + <amOnPage url="{{AdminLoginPage.url}}" stepKey="amOnAdminLoginPage"/> + <fillField selector="{{AdminLoginFormSection.username}}" userInput="{{_ENV.MAGENTO_ADMIN_USERNAME}}" stepKey="fillUsername"/> + <fillField selector="{{AdminLoginFormSection.password}}" userInput="{{_ENV.MAGENTO_ADMIN_PASSWORD}}" stepKey="fillPassword"/> + <click selector="{{AdminLoginFormSection.signIn}}" stepKey="clickOnSignIn"/> + <closeAdminNotification stepKey="closeAdminNotification"/> + <seeInCurrentUrl url="{{AdminLoginPage.url}}" stepKey="seeAdminLoginUrl"/> + </test> +</tests> +``` + +Create the `.../Foo/Test/AdminLoginTest.xml` file: + +```xml +<tests ...> + <test name="AdminLoginTest"> + <annotations> + <skip> + <issueId value="Issue#"/> + </skip> + </annotations> + </test> +</tests> +``` + +The `AdminLoginTest` result corresponds to: + +```xml +<test name="AdminLoginTest"> + <annotations> + <features value="Admin Login"/> + <stories value="Login on the Admin Login page"/> + <title value="You should be able to log into the Magento Admin backend."/> + <description value="You should be able to log into the Magento Admin backend."/> + <severity value="CRITICAL"/> + <testCaseId value="MAGETWO-71572"/> + <group value="example"/> + <group value="login"/> + <skip> + <issueId value="Issue#"/> + </skip> + </annotations> + <amOnPage url="{{AdminLoginPage.url}}" stepKey="amOnAdminLoginPage"/> + <fillField selector="{{AdminLoginFormSection.username}}" userInput="{{_ENV.MAGENTO_ADMIN_USERNAME}}" stepKey="fillUsername"/> + <fillField selector="{{AdminLoginFormSection.password}}" userInput="{{_ENV.MAGENTO_ADMIN_PASSWORD}}" stepKey="fillPassword"/> + <click selector="{{AdminLoginFormSection.signIn}}" stepKey="clickOnSignIn"/> + <closeAdminNotification stepKey="closeAdminNotification"/> + <seeInCurrentUrl url="{{AdminLoginPage.url}}" stepKey="seeAdminLoginUrl"/> +</test> +``` + +## Update a test + +Any change must include information about where it should be inserted relative to other test steps in the scope of the test. + +This is done using the `before` or `after` element. +See the previous examples. + +### Add a test step + +**Use case**: Add `checkOption` before `click` (`stepKey="clickLogin"`) and add `seeInCurrentUrl` after the `click` in the `LogInAsAdminTest` test (in the `.../Backend/Test/LogInAsAdminTest.xml` file) while merging with the `.../Foo/Test/LogInAsAdminTest.xml` file: + +```xml +<tests ...> + <test name="LogInAsAdminTest"> + <amOnPage url="{{AdminLoginPage}}" stepKey="navigateToAdmin"/> + <fillField selector="{{AdminLoginFormSection.username}}" userInput="admin" stepKey="fillUsername"/> + <fillField selector="{{AdminLoginFormSection.password}}" userInput="password" stepKey="fillPassword"/> + <click selector="{{AdminLoginFormSection.signIn}}" stepKey="clickLogin"/> + <see userInput="Lifetime Sales" stepKey="seeLifetimeSales"/> + </test> +</tests> +``` + +Create the `.../Foo/Test/LogInAsAdminTest.xml` file: + +```xml +<tests ...> + <test name="LogInAsAdminTest"> + <checkOption selector="{{AdminLoginFormSection.rememberMe}}" stepKey="checkRememberMe" before="clickLogin"/> + <seeInCurrentUrl url="admin/admin/dashboard/" stepKey="seeAdminUrl" after="clickLogin"/> + </test> +</tests> +``` + +The `LogInAsAdminTest` result corresponds to: + +```xml +<test name="LogInAsAdminTest"> + <amOnPage url="{{AdminLoginPage}}" stepKey="navigateToAdmin"/> + <fillField selector="{{AdminLoginFormSection.username}}" userInput="admin" stepKey="fillUsername"/> + <fillField selector="{{AdminLoginFormSection.password}}" userInput="password" stepKey="fillPassword"/> + <checkOption selector="{{AdminLoginFormSection.rememberMe}}" stepKey="checkRememberMe"/> + <click selector="{{AdminLoginFormSection.signIn}}" stepKey="clickLogin"/> + <seeInCurrentUrl url="admin/admin/dashboard/" stepKey="seeAdminUrl"/> + <see userInput="Lifetime Sales" stepKey="seeLifetimeSales"/> +</test> +``` + +### Remove a test step + +**Use case**: Remove `see` (`stepKey="seeLifetimeSales"`) from the `LogInAsAdminTest` test (in the `.../Backend/Test/LogInAsAdminTest.xml` file) while merging with the `.../Foo/Test/LogInAsAdminTest.xml` file: + +```xml +<tests ...> + <test name="LogInAsAdminTest"> + <amOnPage url="{{AdminLoginPage}}" stepKey="navigateToAdmin"/> + <fillField selector="{{AdminLoginFormSection.username}}" userInput="admin" stepKey="fillUsername"/> + <fillField selector="{{AdminLoginFormSection.password}}" userInput="password" stepKey="fillPassword"/> + <click selector="{{AdminLoginFormSection.signIn}}" stepKey="clickLogin"/> + <see userInput="Lifetime Sales" stepKey="seeLifetimeSales"/> + </test> +</tests> +``` + +Create the `.../Foo/Test/LogInAsAdminTest.xml` file: + +```xml +<tests ...> + <test name="LogInAsAdminTest"> + <remove keyForRemoval="seeLifetimeSales"/> + </test> +</tests> +``` + +The `LogInAsAdminTest` result corresponds to: + +```xml +<test name="LogInAsAdminTest"> + <amOnPage url="{{AdminLoginPage}}" stepKey="navigateToAdmin"/> + <fillField selector="{{AdminLoginFormSection.username}}" userInput="admin" stepKey="fillUsername"/> + <fillField selector="{{AdminLoginFormSection.password}}" userInput="password" stepKey="fillPassword"/> + <click selector="{{AdminLoginFormSection.signIn}}" stepKey="clickLogin"/> +</test> +``` + +### Update a test step + +**Use case**: Change selector in `fillField` (`stepKey="fillPassword"`) of the `LogInAsAdminTest` test (in the `.../Backend/Test/LogInAsAdminTest.xml` file) while merging with the `.../Foo/Test/LogInAsAdminTest.xml` file: + +```xml +<tests ...> + <test name="LogInAsAdminTest"> + <amOnPage url="{{AdminLoginPage}}" stepKey="navigateToAdmin"/> + <fillField selector="{{AdminLoginFormSection.username}}" userInput="admin" stepKey="fillUsername"/> + <fillField selector="{{AdminLoginFormSection.password}}" userInput="password" stepKey="fillPassword"/> + <click selector="{{AdminLoginFormSection.signIn}}" stepKey="clickLogin"/> + <see userInput="Lifetime Sales" stepKey="seeLifetimeSales"/> + </test> +</tests> +``` + +Create the `.../Foo/Test/LogInAsAdminTest.xml` file: + +```xml +<tests ...> + <test name="LogInAsAdminTest"> + <fillField selector="{{AdminLoginFormSection.wrong-password}}" userInput="password" stepKey="fillPassword"/> + </test> +</tests> +``` + +The `LogInAsAdminTest` result corresponds to: + +```xml +<test name="LogInAsAdminTest"> + <amOnPage url="{{AdminLoginPage}}" stepKey="navigateToAdmin"/> + <fillField selector="{{AdminLoginFormSection.username}}" userInput="admin" stepKey="fillUsername"/> + <fillField selector="{{AdminLoginFormSection.wrong-password}}" userInput="password" stepKey="fillPassword"/> + <click selector="{{AdminLoginFormSection.signIn}}" stepKey="clickLogin"/> + <see userInput="Lifetime Sales" stepKey="seeLifetimeSales"/> +</test> +``` + +### Add several test steps {#insert-after} + +**Use case**: Add several actions after the `click` (`stepKey="clickLogin"`) in the `LogInAsAdminTest` test (in the `.../Backend/Test/LogInAsAdminTest.xml` file) while merging with the `.../Foo/Test/LogInAsAdminTest.xml` file: + +```xml +<tests ...> + <test name="LogInAsAdminTest"> + <amOnPage url="{{AdminLoginPage}}" stepKey="navigateToAdmin"/> + <fillField selector="{{AdminLoginFormSection.username}}" userInput="admin" stepKey="fillUsername"/> + <fillField selector="{{AdminLoginFormSection.password}}" userInput="password" stepKey="fillPassword"/> + <click selector="{{AdminLoginFormSection.signIn}}" stepKey="clickLogin"/> + <see userInput="Lifetime Sales" stepKey="seeLifetimeSales"/> + </test> +</tests> +``` + +Create the `.../Foo/Test/LogInAsAdminTest.xml` file: + +```xml +<tests ...> + <test name="LogInAsAdminTest" insertAfter="clickLogin"> + <checkOption selector="{{AdminLoginFormSection.rememberMe}}" stepKey="checkRememberMe"/> + <seeInCurrentUrl url="admin/admin/dashboard/" stepKey="seeAdminUrl"/> + </test> +</tests> +``` + +The `LogInAsAdminTest` result corresponds to: + +```xml +<test name="LogInAsAdminTest"> + <amOnPage url="{{AdminLoginPage}}" stepKey="navigateToAdmin"/> + <fillField selector="{{AdminLoginFormSection.username}}" userInput="admin" stepKey="fillUsername"/> + <fillField selector="{{AdminLoginFormSection.password}}" userInput="password" stepKey="fillPassword"/> + <click selector="{{AdminLoginFormSection.signIn}}" stepKey="clickLogin"/> + <checkOption selector="{{AdminLoginFormSection.rememberMe}}" stepKey="checkRememberMe"/> + <seeInCurrentUrl url="admin/admin/dashboard/" stepKey="seeAdminUrl"/> + <see userInput="Lifetime Sales" stepKey="seeLifetimeSales"/> +</test> +``` + +## Merge action groups + +Merging action groups allows you to extend existing tests by reusing existing action groups, while customizing them for your specific needs. + +### Use case + +Here is an action group for selecting `customerGroup` in the `Cart Price Rules` section. +The controls change drastically in the B2B version, so it was abstracted to an action group so that it may be easily changed if B2B is enabled. + +> Action group for selecting `customerGroup` in the `Cart Price Rules` section: + +```xml +<actionGroup name="selectNotLoggedInCustomerGroup"> + <selectOption selector="{{AdminCartPriceRulesFormSection.customerGroups}}" userInput="NOT LOGGED IN" stepKey="selectCustomerGroup"/> +</actionGroup> +``` + +> B2B Merge file + +```xml +<!-- name matches --> +<actionGroup name="selectNotLoggedInCustomerGroup"> + <!-- removes the original action --> + <remove keyForRemoval="selectCustomerGroup"/> + <!-- adds in sequence of actions to be performed instead--> + <click selector="{{AdminCartPriceRulesFormSection.customerGroups}}" stepKey="expandCustomerGroups"/> + <fillField selector="{{AdminCartPriceRulesFormSection.customerGroupsInput}}" userInput="NOT LOGGED IN" stepKey="fillCustomerGroups"/> + <click selector="{{AdminCartPriceRulesFormSection.customerGroupsFirstResult}}" stepKey="selectNotLoggedInGroup"/> + <click selector="{{AdminCartPriceRulesFormSection.customerGroupsDoneBtn}}" stepKey="closeMultiSelect"/> +</actionGroup> +``` + +## Merge pages + +Use [page] merging to add or remove [sections] in your module. + +To merge [pages][page], the `page name` must be the same as in the base module. +`page name` is set in the `module` attribute. + +### Add a section + +**Use case**: The `FooBackend` module extends the `Backend` module and requires use of two new sections that must be covered with tests. +Add `BaseBackendSection` and `AnotherBackendSection` to the `BaseBackendPage` (`.../Backend/Page/BaseBackendPage.xml` file): + +```xml +<pages ...> + <page name="BaseBackendPage" url="admin" area="admin" module="Magento_Backend"> + <section name="BaseBackendSection"/> + <section name="AnotherBackendSection"/> + </page> +</pages> +``` + +Create the `.../FooBackend/Page/BaseBackendPage.xml` file: + +```xml +<pages ...> + <page name="BaseBackendPage" url="admin" area="admin" module="Magento_Backend"> + <section name="NewExtensionSection"/> + </page> +</pages> +``` + +The `BaseBackendPage` result corresponds to: + +```xml +<page name="BaseBackendPage" url="admin" area="admin" module="Magento_Backend"> + + <section name="BaseBackendSection"/> + <section name="AnotherBackendSection"/> + <section name="NewExtensionSection"/> +</page> +``` + +### Remove a section + +**Use case**: The `FooBackend` module extends the `Backend` module and requires deletion of the `AnotherBackendSection` section (the `.../Backend/Page/BaseBackendPage.xml` file): + +```xml +<page name="BaseBackendPage" url="admin" area="admin" module="Magento_Backend"> + <section name="BaseBackendSection"/> + <section name="AnotherBackendSection"/> +</page> +``` + +Create the `.../FooBackend/Page/BaseBackendPage.xml` file: + +```xml +<page name="BaseBackendPage" url="admin" area="admin" module="Magento_Backend"> + <section name="AnotherBackendSection" remove="true"/> +</page> +``` + +The `BaseBackendPage` result corresponds to: + +```xml +<page name="BaseBackendPage" url="admin" area="admin" module="Magento_Backend"> + <section name="BaseBackendSection"/> +</page> +``` + +## Merge sections + +Use merging to add, remove, or update [elements] in sections. + +All sections with the same _file name_, _section name_, and _element name_ are merged during test generation. + +### Add an element + +**Use case**: The `FooBackend` module extends the `Backend` module and requires a new `mergeElement` element in the `AdminLoginFormSection`. +Add `mergeElement` to the `AdminLoginFormSection`: + +```xml +<sections ...> + <section name="AdminLoginFormSection"> + <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> +</sections> +``` + +Create the `.../FooBackend/Section/AdminLoginFormSection.xml` file: + +```xml +<sections ...> + <section name="AdminLoginFormSection"> + <element name="mergeElement" type="input" selector="#selector"/> + </section> +</sections> +``` + +The `AdminLoginFormSection` result corresponds to: + +```xml +<section name="AdminLoginFormSection"> + <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"/> + <element name="mergeElement" type="input" selector="#selector"/> +</section> +``` + +### Remove an element + +**Use case**: The `FooBackend` module extends the `Backend` module and requires deletion of `username` in the `AdminLoginFormSection`. +Remove `username` from the `AdminLoginFormSection`: + +```xml +<sections ...> + <section name="AdminLoginFormSection"> + <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> +</sections> +``` + +Create the `.../FooBackend/Section/AdminLoginFormSection.xml` file: + +```xml +<sections ...> + <section name="AdminLoginFormSection"> + <element name="username" type="input" remove="true"/> + </section> +</sections> +``` + +The `AdminLoginFormSection` result corresponds to: + +```xml +<section name="AdminLoginFormSection"> + <element name="password" type="input" selector="#login"/> + <element name="signIn" type="button" selector=".actions .action-primary" timeout="30"/> +</section> +``` + +### Update an element + +**Use case**: The `FooBackend` module extends the `Backend` module and requires change of a selector in `username` in the `AdminLoginFormSection`. +Update `username` in the `AdminLoginFormSection` (the `.../Backend/Section/AdminLoginFormSection.xml` file): + +```xml +<sections ...> + <section name="AdminLoginFormSection"> + <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> +</sections> +``` + +Create the `.../FooBackend/Section/AdminLoginFormSection.xml` file: + +```xml +<sections ...> + <section name="AdminLoginFormSection"> + <element name="username" type="input" selector="#newSelector"/> + </section> +</sections> +``` + +The `AdminLoginFormSection` result corresponds to: + +```xml +<section name="AdminLoginFormSection"> + <element name="username" type="input" selector="#newSelector"/> + <element name="password" type="input" selector="#login"/> + <element name="signIn" type="button" selector=".actions .action-primary" timeout="30"/> +</section> +``` + +## Merge data + +You can add or update `<data>` elements within an `<entity>`. +Removal of individual `<data>` tags is not supported. + +Entities and data with the same _file name_, _entity name and type_, and _data key_ are merged during test generation. + +### Add data + +**Use case**: The `FooSample` module extends the `Sample` module and requires a new data item `thirdField` in the `_defaultSample` entity. +Add `<data key="thirdField">field3</data>` to the `_defaultSample` (the `.../Sample/Data/SampleData.xml` file): + +```xml +<entities ...> + <entity name="_defaultSample" type="testData"> + <data key="firstField">field1</data> + <data key="secondField">field2</data> + </entity> +</entities> +``` + +Create the `.../FooSample/Data/SampleData.xml` file: + +```xml +<entities ...> + <entity name="sampleData" type="testData"> + <data key="thirdField">field3</data> + </entity> +</entities> +``` + +The `_defaultSample` result corresponds to: + +```xml +<entity name="_defaultSample" type="testData"> + <data key="firstField">field1</data> + <data key="secondField">field2</data> + <data key="thirdField">field3</data> +</entity> +``` + +### Update data + +**Use case**: The `FooSample` module extends the `Sample` module and requires changing the `firstField` data item in the `_defaultSample` entity. +Change `firstField` to `<data key="firstField">overrideField</data>` in the `_defaultSample` (the `.../Sample/Data/SampleData.xml` file): + +```xml +<entities ...> + <entity name="_defaultSample" type="testData"> + <data key="firstField">field1</data> + <data key="secondField">field2</data> + </entity> +</entity> +``` + +Create the `.../FooSample/Data/SampleData.xml` file: + +```xml +<entities ...> + <entity name="_defaultSample" type="testData"> + <data key="firstField">overrideField</data> + </entity> +</entity> +``` + +The `_defaultSample` results corresponds to: + +```xml +<entity name="_defaultSample" type="testData"> + <data key="firstField">overrideField</data> + <data key="secondField">field2</data> +</entity> +``` + +<!-- Link definitions --> + +[`codecept`]: ./commands/codeception.html +[`mftf`]: ./commands/mftf.html +[`<data>`]: ./data.html +[elements]: ./section.html#element-tag +[`<pages>`]: ./page.html +[`<sections>`]: ./section.html +[`<tests>`]: ./test.html diff --git a/docs/metadata.md b/docs/metadata.md new file mode 100644 index 000000000..6e507660f --- /dev/null +++ b/docs/metadata.md @@ -0,0 +1,598 @@ +--- +mftf-release: 2.2.0 +redirect_from: /guides/v2.3/magento-functional-testing-framework/2.3/metadata.html +--- + +# Metadata + +_This topic was updated due to the {{page.mftf-release}} MFTF release._ +{: style="text-align: right"} + +In this topic we talk about handling entities that you need in your tests (such as categories, products, wish lists, and similar) using the MFTF. +Using data handling actions like [`createData`], [`deleteData`], [`updateData`], and [`getData`], you are able to create, delete, update, and read entities for your tests. +The framework enables you to send HTTP requests with these statically defined data entities: + +- [Sending a REST API request][rest request] +- [Handling a REST API response][rest response] +- [Sending an HTML form encoded in URL][html form] + +You have probably noticed that some modules in acceptance functional tests contain a directory, which is called `Metadata` . + +Example of a module with _Metadata_: + +```tree +Wishlist +├── Data +├── Metadata +├── Page +├── Section +└── Test +``` + +This directory contains XML files with metadata required to create a valid request to handle an entity defined in `dataType`. +A metadata file contains a list of operations with different types (defined in `type`). +Each [operation] includes: + +- The set of adjustments for processing a request in [attributes][operation], and in some cases, a response (see `successRegex` and `returnRegex` in [reference details][operation]). +- The type of body content encoding in [contentType]. +- The body of the request represented as a tree of objects, arrays, and fields. + +When a test step requires handling the specified data entity, the MFTF performs the following steps: + +- Reads input data (`<data/>`) and the type (the `type` attribute) of the specified [entity]. +- Searches the metadata operation for the `dataType` that matches the entity's `type`. For example, `<entity type="product">` matches `<operation dataType="product"`. +- Forms a request of the operation and the input data of the entity according to matching metadata. +- Stores a response and provides access to its data using MFTF variables syntax in XML. + +The following diagram demonstrates the XML structure of a metadata file: +{% include_relative img/metadata-dia.svg %} + +## Format {#format} + +```xml +<?xml version="1.0" encoding="UTF-8"?> +<operations xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataOperation.xsd"> + <operation name="" + dataType="" + type="" + auth="" + url="" + method=""> + <contentType></contentType> + <object key="" dataType=""> + <field key="">integer</field> + <field key="">string</field> + <field key="">boolean</field> + <array key=""> + <value>string</value> + </array> + </object> + </operation> +``` + +## Principles {#principles} + +1. A `dataType` value must match the `type` value of the corresponding entity. +2. A file name should contain data type split with `_` and must end with `-meta`. + Example: `product_attribute-meta.xml`. +3. A metadata file may contain different types of operations (`type`) with the same data entity (`dataType`). + +Example: + + ```xml + <operations> + <operation type="create" dataType="category"> + ... + </operation> + <operation type="update" dataType="category"> + ... + </operation> + <operation type="delete" dataType="category"> + ... + </operation> + <operation type="get" dataType="category"> + ... + </operation> + </operations> + ``` + +## Handling entities using REST API {#handling-with-api} + +### Sending a REST API request + +The MFTF allows you to handle basic CRUD operations with an object using [Magento REST API][api reference] requests. +To convert a request to the MFTF format, wrap the corresponding REST API request into XML tags according to the [Reference documentation][reference]. + +{% include note.html +type="info" +content="GET is used for retrieving data from objects. + + POST is used for creating new objects. + + PUT is used for updating objects. + + DELETE is used for deleting objects." %} + +This is an example of how to handle a category using REST API operations provided by the `catalogCategoryRepositoryV1` service. + +![REST API operations provided by catalogCategoryRepositoryV1][catalogCategoryRepositoryV1 image] + +The above screenshot from the [Magento REST API Reference][api reference] demonstrates a list of available operations to: + +- Delete a category by its identifier (`method="DELETE"`) +- Get information about a category by its ID (`method="GET"`) +- [Create a new category] (`method="POST"`) +- Update category data by its ID (`method="PUT"`) + +We assume that our `.env` file sets `MAGENTO_BASE_URL=https://example.com/` and `MAGENTO_BACKEND_NAME=admin`. + +#### Create a simple category {#create-object-as-adminOauth} + +Let's see what happens when you create a category: + +```xml +<createData entity="_defaultCategory" stepKey="createPreReqCategory"/> +``` + +The MFTF searches in the _Data_ directory an entity with `<entity name="_defaultCategory">` and reads `type` of the entity. +If there are more than one entity with the same name, all of the entities are merged. + +_Catalog/Data/CategoryData.xml_: + +```xml +<entity name="_defaultCategory" type="category"> + <data key="name" unique="suffix">simpleCategory</data> + <data key="name_lwr" unique="suffix">simplecategory</data> + <data key="is_active">true</data> +</entity> +``` + +Here, `type` is equal to `"category"`, which instructs the MFTF to search an operation with `dataType="category"`. +Since the action is __to create__ a category, the MFTF will also search for operation with `type="create"` in _Metadata_ for `dataType="category"`. + +_Catalog/Metadata/category-meta.xml_: + +```xml +<operation name="CreateCategory" dataType="category" type="create" auth="adminOauth" url="/V1/categories" method="POST"> + <contentType>application/json</contentType> + <object key="category" dataType="category"> + <field key="parent_id">integer</field> + <field key="name">string</field> + <field key="is_active">boolean</field> + <field key="position">integer</field> + <field key="level">integer</field> + <field key="children">string</field> + <field key="created_at">string</field> + <field key="updated_at">string</field> + <field key="path">string</field> + <field key="include_in_menu">boolean</field> + <array key="available_sort_by"> + <value>string</value> + </array> + <field key="extension_attributes">empty_extension_attribute</field> + <array key="custom_attributes"> + <value>custom_attribute</value> + </array> + </object> +</operation> +``` + +(The corresponding operation is provided by _catalogCategoryRepositoryV1_ in [REST API][api reference].) + +The following is encoded in `<operation>`: + +- `name="CreateCategory"` defines a descriptive name of the operation, which is used for merging if needed. +- `dataType="category"` defines a relation with data entities with input data for a Category (`<entity type="category">`). +- `auth="adminOauth"` defines OAuth authorization, which is required for the Admin area. +- `url="/V1/categories"` defines a routing URL to the corresponding service class. + (The request will be sent to `https://example.com/rest/V1/categories` if `MAGENTO_BASE_URL=https://example.com/` and `MAGENTO_BACKEND_NAME=admin` are set in the _acceptance/.env_ configuration file.) +- `method="POST"` defines a POST method of the HTTP request. + +`<contentType>application/json</contentType>` defines a content type of the REST API request, which is set as `application/json` here. + +The parameter that declares a body of the request is _catalogCategoryRepositoryV1SavePostBody_. +Using the [Reference], we can trace how the JSON request was converted into XML representation. + +{% include note.html +type="info" +content="Comments in the example below are used to demonstrate relation between JSON request and MFTF metadata in XML. +JSON does not support comments."%} + +Model schema for _catalogCategoryRepositoryV1SavePostBody_ with XML representation of _Catalog/Metadata/category-meta.xml_ in comments: +{:#catalogCategoryRepositoryV1SavePostBody} + +```json +{ // XML representation in the MFTF metadata format (see 'Catalog/Metadata/category-meta.xml') + "category": { // <object key="category" dataType="category"> + "id": 0, // Skipped, because Category ID is not available on UI when you create a new category. + "parent_id": 0, // <field key="parent_id">integer</field> + "name": "string", // <field key="name">string</field> + "is_active": true, // <field key="is_active">boolean</field> + "position": 0, // <field key="position">integer</field> + "level": 0, // <field key="level">integer</field> + "children": "string", // <field key="children">string</field> + "created_at": "string", // <field key="created_at">string</field> + "updated_at": "string", // <field key="updated_at">string</field> + "path": "string", // <field key="path">string</field> + "available_sort_by": [ // <array key="available_sort_by"> + "string" // <value>string</value> + ], // </array> + "include_in_menu": true, // <field key="include_in_menu">boolean</field> + "extension_attributes": {}, // <field key="extension_attributes">empty_extension_attribute</field>, where 'empty_extension_attribute' is a reference to operation with 'dataType="empty_extension_attribute"' (see 'Catalog/Metadata/empty_extension_attribute-meta.xml') + "custom_attributes": [ // <array key="custom_attributes"> + { // <value>custom_attribute</value>, where 'custom_attribute' is a reference to operation with 'dataType="custom_attribute"' (see 'Catalog/Metadata/custom_attribute-meta.xml') + "attribute_code": "string", + "value": "string" + } + ] // </array> + } // </object> +} +``` + +So, the body of a REST API request that creates a simple category is the following: + +```json +{ // XML representation of input data used to create a simple category ("Catalog/Data/CategoryData.xml") + "category": { // <entity name="_defaultCategory" type="category"> + "name": "simpleCategory_0986576456", // <data key="name" unique="suffix">simpleCategory</data> + "is_active": true // <data key="is_active">true</data> + } // </entity> +} +``` + +#### Create an object as a guest {#create-object-as-anonymous} + +The corresponding test step is: + +```xml +<createData entity="guestCart" stepKey="createGuestCart"/> +``` + +The MFTF searches in the _Data_ directory an entity with `<entity name="guestCart">` and reads `type`. + +_Quote/Data/GuestCartData.xml_: + +```xml +<entity name="guestCart" type="guestCart"> +</entity> +``` + +`type="guestCart"` points to the operation with `dataType=guestCart"` and `type="create"` in the _Metadata_ directory. + +_Catalog/Data/CategoryData.xml_: + +```xml +<operation name="CreateGuestCart" dataType="guestCart" type="create" auth="anonymous" url="/V1/guest-carts" method="POST"> + <contentType>application/json</contentType> +</operation> +``` + +As a result, the MFTF sends an unauthorized POST request with an empty body to the `https://example.com/rest/V1/guest-carts` and stores the single string response that the endpoint returns. + +### Handling a REST API response {#rest-response} + +There are cases when you need to reuse the data that Magento responded with to your POST request. + +Let's see how to handle data after you created a category with custom attributes: + +```xml +<createData entity="customizedCategory" stepKey="createPreReqCategory"/> +``` + +The MFTF receives the corresponding JSON response and enables you to reference its data using a variable of format: + +**$** _stepKey_ **.** _JsonKey_ **$** + +Example: + +```xml +$createPreReqCategory.id$ +``` + +And for a custom attribute: + +**$** _stepKey_ **.custom_attributes[** _attribute key_ **]$** + +Example: + +```xml +$createPreReqCategory.custom_attributes[is_anchor]$ +``` + +The following example of response in JSON demonstrates how to reference data on the root level and as data in custom attributes: + +```json +{ + "id": 7, //$createPreReqCategory.id$ + "parent_id": 2, //$createPreReqCategory.parent_id$ + "name": "simpleCategory_0986576456", //$createPreReqCategory.is_active$ + "is_active": true, + "position": 5, + "level": 2, + "children": "", + "created_at": "2018-05-08 14:27:18", + "updated_at": "2018-05-08 14:27:18", + "path": "1/2/7", + "available_sort_by": [], + "include_in_menu": true, + "custom_attributes": [ + { + "attribute_code": "is_anchor", + "value": "1" //$createPreReqCategory.custom_attributes[is_anchor]$ + }, + { + "attribute_code": "path", + "value": "1/2/7" //$createPreReqCategory.custom_attributes[path]$ + }, + { + "attribute_code": "children_count", + "value": "0" + }, + { + "attribute_code": "url_key", + "value": "simplecategory5af1b41cd58fb4" //$createPreReqCategory.custom_attributes[url_key]$ + }, + { + "attribute_code": "url_path", + "value": "simplecategory5af1b41cd58fb4" + } + ], +} +} +``` + +## Handling entities using HTML forms {#using-html-forms} + +For cases when REST API is not applicable, you may use [HTML forms] (when all object parameters are encoded in a URL as `key=name` attributes). +There are two different attributes to split access to different areas: + +- `auth="adminFormKey"` is used for objects in an Admin area. +- `auth="customerFormKey"` is used for objects in a storefront. + +You are able to create assurances with `successRegex`, and even return values with `returnRegex`. + +### Create an object in Admin {#create-object-as-adminFormKey} + +The `CreateStoreGroup` operation is used to persist a store group: + +Source file is _Store/Metadata/store_group-meta.xml_: + +```xml +<operation name="CreateStoreGroup" dataType="group" type="create" auth="adminFormKey" url="/admin/system_store/save" method="POST" successRegex="/messages-message-success/" > + <contentType>application/x-www-form-urlencoded</contentType> + <object dataType="group" key="group"> + <field key="group_id">string</field> + <field key="name">string</field> + <field key="code">string</field> + <field key="root_category_id">integer</field> + <field key="website_id">integer</field> + </object> + <field key="store_action">string</field> + <field key="store_type">string</field> +</operation> +``` + +The operation is called when `<createData>` (`type="create"`) points to a data entity of type `"group"` (`dataType="group"`). +It sends a POST request (`method="POST"`) to `http://example.com/admin/system_store/save` (`url="/admin/system_store/save"`) that is authorized for the Admin area (`auth="adminFormKey"`). +The request contains HTML form data encoded in the [application/x-www-form-urlencoded] content type (`<contentType>application/x-www-form-urlencoded</contentType>`). +If the returned HTML code contains the `messages-message-success` string, it is resolved as successful. + +The operation enables you to assign the following form fields: + +- `group/group_id` +- `group/name` +- `group/code` +- `group/root_category_id` +- `group/website_id` +- `store_action` +- `store_type` + +### Create an object in storefront {#create-object-as-customerFormKey} + +The MFTF uses the `CreateWishlist` operation to create a wish list on storefront: + +Source file is _Wishlist/Metadata/wishlist-meta.xml_ + +```xml +<operation name="CreateWishlist" dataType="wishlist" type="create" auth="customerFormKey" url="/wishlist/index/add/" method="POST" successRegex="" returnRegex="~\/wishlist_id\/(\d*?)\/~" > + <contentType>application/x-www-form-urlencoded</contentType> + <field key="product">integer</field> + <field key="customer_email">string</field> + <field key="customer_password">string</field> +</operation> +``` + +The operation is used when `<createData>` (`type="create"`) points to a data entity of type `"wishlist"` (`dataType="wishlist"`). +It sends a POST request (`method="POST"`) to `http://example.com/wishlist/index/add/` (`url="wishlist/index/add/"`) as a customer (`auth="customerFormKey"`). +The request contains HTML form data encoded in the [application/x-www-form-urlencoded] content type (`<contentType>application/x-www-form-urlencoded</contentType>`). +If the returned HTML code contains a string that matches the regular expression `~\/wishlist_id\/(\d*?)\/~`, it returns the matching value. + +The operation assigns three form fields: + +- `product` +- `customer_email` +- `customer_password` + +## Reference + +### operations {#operations-tag} + +Root element that points to the corresponding XML Schema. + +### operation {#operation-tag} + +| Attribute | Type | Use | Description | +|-----------------|------------------------------------------------------------------------------|----------|----------------------------------------------------------------------------------------------------------------------------------------------| +| `name` | string | optional | Name of the operation. | +| `dataType` | string | required | Data type of the operation. It refers to a `type` attribute of data entity that will be used as a source of input data. | +| `type` | Possible values: `create`, `delete`, `update`, `get`. | required | Type of operation. | +| \*`url` | string | optional | A routing URL of the operation. Example value: `"/V1/categories"`. | +| \*\*`auth` | Possible values: `adminOath`, `adminFormKey`, `customerFormKey`, `anonymous` | optional | Type of authorization of the operation. | +| `method` | string | optional | HTTP method of the operation. Possible values: `POST`, `DELETE`, `PUT`, `GET`. | +| `successRegex` | string | optional | Determines if the operation was successful. Parses the HTML body in response and asserts if the value assigned to the `successRegex` exists. | +| `returnRegex` | string | optional | Determines if the response contains the matching value to return. | +| `removeBackend` | boolean | optional | Removes backend name from requested URL. Applicable when `auth="adminFormKey"`. | +| `filename` | string | optional | | + +- \*`url` - full URL is a concatenation of _ENV.baseUrl_ + `/rest/` + _url_. + To reuse data of a required entity or returned response use a field key wrapped in curly braces such as `{sku}`. + When the data to reuse is of a different type, declare also the type of data such as `{product.sku}`. + Example: `"/V1/products/{product.sku}/media/{id}"`. + +- \*\*`auth` - available values: + + - `adminOath` is used for REST API persistence in the Admin area with [OAuth-based authentication][oauth]. + - `adminFormKey` is used for HTML form persistence in the Admin area. + - `customerFormKey` is used for HTML form persistence in the Customer area. + - `anonymous` is used for REST API persistence without authorization. + +### contentType {#contentType-tag} + +Sets one of the following operation types: + +- `application/json` is used for REST API operations. +- `application/x-www-form-urlencoded` is used for HTML form operations. + +### object {#object-tag} + +Representation of a complex entity that may contain fields, arrays, and objects. +An object must match the [entity] of the same `type`. + +| Attribute | Type | Use | Description | +| ---------- | ------- | -------- | ---------------------------------------------------------------------------------------------- | +| `key` | string | optional | Name of the object. | +| `dataType` | string | required | Type of the related [entity]. | +| `required` | boolean | optional | Determines if the object is required or not. It must match the Magento REST API specification. | + +### field {#field-tag} + +Representation of HTML form or REST API fields. + +| Attribute | Type | Use | Description | +| ---------- | ------- | -------- | --------------------------------------------------------------------------------------------- | +| `key` | string | required | Name of the field. It must match the field name of the related [entity]. | +| `type` | string | optional | Type of the value. It may contain a primitive type or the type of another operation. | +| `required` | boolean | optional | Determines if the field is required or not. It must match the Magento REST API specification. | + +### array {#array-tag} + +Representation of an array. + +| Attribute | Type | Use | Description | +| --------- | ------ | -------- | ------------------ | +| `key` | string | required | Name of the array. | + +It contains one or more `value` elements. + +### value {#value-tag} + +Declares a data type for items within `<array>`. + +#### Example of an array with value of a primitive data type + +Metadata declaration of the operation schema: + +```xml +<array key="tax_rate_ids"> + <value>integer</value> +</array> +``` + +The value can contain one or more items. + +Data entity with the corresponding assignment: + +```xml +<array key="tax_rate_ids"> + <item>1</item> + <item>2</item> +</array> +``` + +- Resulted JSON request: + +```json +"tax_rate_ids": + [ + "item": 1, + "item": 2 + ] +``` + +#### Example of an array containing data entities + +```xml +<array key="product_options"> + <value>product_option</value> +</array> +``` + +The value declares the `product_options` array that contains one or more entities of the `product_option` data type. + +#### Example of an array containing a particular data field + +```xml +<array key="tax_rate_ids"> + <value>tax_rate.id</value> +</array> +``` + +The value declares the `tax_rate_ids` array that contains one or more `id` fields of the `tax_rate` data type entity. + +### header {#header-tag} + +An additional parameter in REST API request. + +| Attribute | Type | Use | Description | +| --------- | ------ | -------- | ---------------------------- | +| `param` | string | required | A REST API header parameter. | + +```xml +<header param="status">available</header> +``` + +### param {#param-tag} + +An additional parameter in URL. + +| Attribute | Type | Use | Description | +| --------- | ------ | -------- | -------------------------- | +| `key` | string | required | Name of the URL parameter. | + +Example: + +```xml +<param key="status">someValue</param> +``` + +<!-- LINK DEFINITIONS --> + +[actions]: test/actions.html +[api reference]: {{ site.gdeurl23 }}rest/bk-rest.html +[application/x-www-form-urlencoded]: https://www.w3.org/TR/html401/interact/forms.html#h-17.13.4.1 +{:target="_blank"} +[catalogCategoryRepositoryV1 image]: img/catalogCategoryRepository-operations.png +[catalogCategoryRepositoryV1SavePostBody]: #catalogCategoryRepositoryV1SavePostBody +[contentType]: #contentType-tag +[Create a new category]: #create-object-as-adminOauth +[createData]: test/actions.html#createdata +[delete a category by its ID]: #delete-object-as-adminOauth +[deleteData]: test/actions.html#deletedata +[entity]: data.html#entity-tag +[get information about a category by its ID]: #get-object-as-adminOauth +[getData]: test/actions.html#getdata +[HTML forms]: https://www.w3.org/TR/html401/interact/forms.html +{:target="_blank"} +[oauth]: {{ site.gdeurl23 }}get-started/authentication/gs-authentication-oauth.html +{:target="_blank"} +[operation]: #operation-tag +[reference]: #reference +[rest request]: #handling-with-api +[html form]: #using-html-forms +[update category data by its ID]: #update-object-as-adminOauth +[updateData]: test/actions.html#updatedata +[rest response]: #rest-response + +*[CRUD]: Create Read Update Delete +*[MFTF]: Magento Functional Testing Framework diff --git a/docs/page.md b/docs/page.md new file mode 100644 index 000000000..f52913e40 --- /dev/null +++ b/docs/page.md @@ -0,0 +1,185 @@ +--- +mftf-release: 2.2.0 +redirect_from: /guides/v2.3/magento-functional-testing-framework/2.3/page.html +--- + +# Page structure + +_This topic was updated due to the {{page.mftf-release}} MFTF release._ +{: style="text-align: right"} + +The MFTF uses a modified concept of [PageObjects], which models the testing areas of your page as objects within the code. +This reduces occurrences of duplicated code and enables you to fix things quickly, in one place, when things change. +You define the contents of a page, for reference in a [`<test>`], at both the [`<page>`] and [`<section>`] level. + +The `pageObject` lists the URL of the `page` and the `sections` that it contains. +You can reuse `sections` to define the elements on a page that are exercisable, while also ensuring a single source of truth to help maintain consistency. + +{% include note.html +type="tip" +content="Avoid hard-coded location selectors from tests to increase the maintainability and readability of the tests, as well as improve the test execution output and logging." +%} + +Two types of pages are available: + {%raw%} + +- Page with `url` declared as a constant string or [explicit page] - In a test it is called in a format like `{{NameOfPage.url}}`, where `NameOfPage` is a value of `name` in the corresponding page declaration `*.xml` file. +- Page with `url` declared as a string with one or more variables or [parameterized page] +- In a test it is called using a format like `{{NameOfPage.url(var1, var2, ...)}}`, where `var1, var2` etc. are parameters that will be substituted in the `url` of the corresponding `<page>` declaration. + +{%endraw%} + +The following diagram shows the XML structure of an MFTF page: + +{% include_relative img/page-dia.svg %} + +## Format + +The format of `<page>` is: + +```xml +<?xml version="1.0" encoding="UTF-8"?> + +<pages xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/PageObject.xsd"> + <page name="" url="" module="" area=""> + <section name=""/> + <section name=""/> + </page> +</pages> +``` + +## Principles + +The following conventions apply to MFTF pages: + +- `<page>` name is the same as the file name. +- `<page>` name must be alphanumeric. +- `*Page.xml` is stored in the _Page_ directory of a module. +- The name format is `{Admin|Storefront}{PageDescription}Page.xml`. + +The `.url` attribute is required when using the page for [actions] that require the URL argument. + +## Page examples + +These examples demonstrate explicit and parameterized pages and include informative explanations. + +### Explicit page + +Example (_Catalog/Page/AdminCategoryPage.xml_ file): + +```xml +<?xml version="1.0" encoding="UTF-8"?> + +<pages xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="../../../../../..dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/PageObject.xsd"> + <page name="AdminCategoryPage" url="catalog/category/" module="Magento_Catalog" area="admin"> + <section name="AdminCategorySidebarActionSection"/> + <section name="AdminCategorySidebarTreeSection"/> + <section name="AdminCategoryBasicFieldSection"/> + <section name="AdminCategorySEOSection"/> + </page> +</pages> +``` + +In this example, the _Catalog/Page/AdminCategoryPage.xml_ file declares a page with the name `AdminCategoryPage`. +It will be merged with the other `AdminCategoryPage` pages from other modules. + +The corresponding web page is generated by the Magento Catalog module and is called by the `baseUrl` + `backendName` + `catalog/category/` URl. + +The `AdminCategoryPage` declares four [sections][section]: + +- `AdminCategorySidebarActionSection` - located in the `Catalog/Section/AdminCategorySidebarActionSection.xml` file +- `AdminCategorySidebarTreeSection` - located in the `Catalog/Section/AdminCategorySidebarTreeSection.xml` file +- `AdminCategoryBasicFieldSection` - located in the `Catalog/Section/AdminCategoryBasicFieldSection.xml` file +- `AdminCategorySEOSection` - located in the `Catalog/Section/AdminCategorySEOSection.xml` file + +The following is an example of a call in test: +{%raw%} + +```xml +<amOnPage url="{{AdminCategoryPage.url}}" stepKey="navigateToAdminCategory"/> +``` + +### Parameterized page + +Example (_Catalog/Page/StorefrontCategoryPage.xml_ file): + +```xml +<?xml version="1.0" encoding="UTF-8"?> + +<pages xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="../../../../../..dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/PageObject.xsd"> + <page name="StorefrontCategoryPage" url="/{{var1}}.html" module="Magento_Catalog" parameterized="true" area="storefront"> + <section name="StorefrontCategoryMainSection"/> + </page> +</pages> +``` + +This example shows the page with the name `StorefrontCategoryPage`. +It will be merged with the other `StorefrontCategoryPage` pages from other modules. + +The following is an example of a call in test: + +```xml +<amOnPage url="{{StorefrontCategoryPage.url($$createPreReqCategory.name$$)}}" stepKey="navigateToCategoryPage"/> +``` + +The `StorefrontCategoryPage` page is declared as parameterized, where the `url` contains a `{{var1}}` parameter. + +The corresponding web page is generated by the Magento Catalog module and is called by the `baseUrl`+`/$$createPreReqCategory.name$$.html` URl. + +`{{var1}}` is substituted with the `name` of the previously created category in the `createPreReqCategory` action. + +See also [`<createData>`]. + +**** + +The `StorefrontCategoryPage` page declares only the `StorefrontCategoryMainSection` [section], which is located in the `Catalog/Section/StorefrontCategoryMainSection.xml` file. + +## Elements reference + +There are several XML elements that are used in `<page>` in the MFTF. + +### pages {#pages-tag} + +`<pages>` are elements that point to the corresponding XML Schema location. +It contains one or more `<page>` elements. + +### page {#page-tag} + +`<page>` contains a sequence of UI sections in a page. + +Attributes|Type|Use|Description +---|---|---|--- +`name`|string|required|Unique page name identifier. +`url`|string|required|URL path (excluding the base URL) for the page. Use parameterized notation (`{{var1}}`) for replaceable parameters, such as the edit page for a persisted entity that is based on an ID or a name. +`module`|string|required|Name of the module to which the page belongs. The name must be prefixed with a vendor name. It corresponds to the parent directory where the module with tests is stored. Example: `"Magento_Catalog"`. +`area`|string|required|The area where this page lives. Three possible values: `admin` prepends `BACKEND_NAME` to `url`, `storefront` does not prepend anything to `url`, `external` flags the page for use with `amOnUrl`. The `url` provided must be a full URL, such as `http://myFullUrl.com/`, instead of the URL for a Magento page. +`parameterized`|boolean |optional|Include and set to `"true"` if the `url` for this page has parameters that need to be replaced for proper use. +`remove`|boolean|optional|The default value is `"false"`. Set to `"true"` to remove this element during parsing. + +`<page>` may contain several [`<section>`] elements. + +### section {#section-tag} + +`<section>` contains the sequence of UI elements. +A section is a reusable piece or part of a page. + +Attributes|Type|Use|Description +---|---|---|--- +`name`|string|required|Unique section name identifier. +`remove`|boolean|optional|The default value is `"false"`. Set to `"true"` to remove this element during parsing. + +{%endraw%} + +<!-- Link definitions --> +[`<createData>`]: test/actions.html#createdata +[`<page>`]: #page-tag +[`<section>`]: #section-tag +[`<test>`]: test.html +[actions]: test/actions.html +[explicit page]: #explicit-page +[PageObjects]: https://github.com/SeleniumHQ/selenium/wiki/PageObjects +[parameterized page]: #parameterized-page +[section]: section.html diff --git a/docs/reporting.md b/docs/reporting.md new file mode 100644 index 000000000..a71c591a6 --- /dev/null +++ b/docs/reporting.md @@ -0,0 +1,359 @@ +--- +--- + +# Reporting + +The Magento Functional Testing Framework provides two types of reporting: + +- Inline reporting that you can view in the terminal as you run [`mftf`][mftf] or [`codecept`][codecept] CLI commands. +- HTML reports that you can view using the [Allure Framework][] after a test run completes. + +When you run a test, MFTF copies all reporting artifacts to the `dev/tests/acceptance/tests/_output` subdirectory in the Magento root directory. +The directory contains: + +- `allure-results/` that is a directory generated and served by the Allure Framework. +- `failed` that is a text file containing relative paths to failed tests after the last test run. + The paths are relative to `dev/tests/acceptance/`. +- `.html` and `.png` files that are screenshots of fails in HTML and PNG formats. + To cleanup the `_output/` directory, remove them manually. + +The `mftf` tool logs output continuously to the `dev/tests/acceptance/mftf.log` file. + +## Command line + +The MFTF reports about its progress during test run when you run the `mftf` CLI tool with [`run:test`][] or [`run:group`][] commands. + +The report can contain three main parts: + +- Pre-run checks: + - Environment check, such as PHP warnings, etc. + - XML test validation like deprecation warnings such as missing required components in XML tests. +- Codeception report which is the progress report for each test. +- Total results of the test run such as number of tests, assertions, and failures. + +To manage the level of verbosity, use `-v` or `--verbose` flag in the `mftf` commands. +To enable verbosity using the `codecept` commands, refer to the Codeception [Console Commands][codeception]. + +The following sections demonstrate an example interpretation of a complete log separated into chunks. + +### Pre-run check report + +First, the MFTF reports about issues with environment. +In our case, there is an issue with PHP library loading. + +```terminal +PHP Warning: PHP Startup: Unable to load dynamic library '/usr/local/lib/php/pecl/20160303/php_intl.dll' - dlopen(/usr/local/lib/php/pecl/20160303/php_intl.dll, 9): image not found in Unknown on line 0 +``` + +Next, the MFTF returns `DEPRECATION` reports alerting you that required test components are missing in XML test definitions. + +```terminal +DEPRECATION: Test AdminFilteringCategoryProductsUsingScopeSelectorTest is missing required annotations.{"testName":"AdminFilteringCategoryProductsUsingScopeSelectorTest","missingAnnotations":"stories"} +DEPRECATION: Test AdminAbleToShipPartiallyInvoicedItemsTest is missing required annotations.{"testName":"AdminAbleToShipPartiallyInvoicedItemsTest","missingAnnotations":"stories"} +DEPRECATION: Test AdminRemoveProductWeeeAttributeOptionTest is missing required annotations.{"testName":"AdminRemoveProductWeeeAttributeOptionTest","missingAnnotations":"stories"} +Generate Tests Command Run +``` + +`Generate Tests Command Run` indicates the moment when the MFTF has run the tests generation command actually. + +### Test execution report + +A test execution report is generated by Codeception and includes configuration information, scenario execution steps, and PASSED/FAIL verdict after each test completion. + +#### General information + +The general information can be useful for MFTF contributors, but can be ignored by a test writer. + +Let's consider the general part of the following test execution report: + +```terminal +==== Redirecting to Composer-installed version in vendor/codeception ==== +Codeception PHP Testing Framework v2.3.9 +Powered by PHPUnit 6.5.13 by Sebastian Bergmann and contributors. + +Magento\FunctionalTestingFramework.functional Tests (2) ------------------------ +Modules: \Magento\FunctionalTestingFramework\Module\MagentoWebDriver, \Magento\FunctionalTestingFramework\Helper\Acceptance, \Magento\FunctionalTestingFramework\Helper\MagentoFakerData, \Magento\FunctionalTestingFramework\Module\MagentoRestDriver, PhpBrowser, \Magento\FunctionalTestingFramework\Module\MagentoSequence, \Magento\FunctionalTes +``` + +After the test generation command (mentioned in the previous section), the MFTF delegates control to the `vendor/codeception` tool, which is the `Codeception PHP Testing Framework` of version `2.3.9` that uses `PHPUnit` of version `6.5.13`. + +The tool runs `2 Tests` using the configuration defined in the `functional` suite under the `Magento\FunctionalTestingFramework` namespace. +The corresponding configuration file is `acceptance/tests/functional.suite.yml`. +It enables `Modules: \Magento\FunctionalTestingFramework\Module\MagentoWebDriver, \Magento\FunctionalTestingFramework\Helper\Acceptance, \Magento\FunctionalTestingFramework\Helper\MagentoFakerData, \Magento\FunctionalTestingFramework\Module\MagentoRestDriver, PhpBrowser, \Magento\FunctionalTestingFramework\Module\MagentoSequence, ...` + +#### Passed tests + +The next chunk of the log reports about test execution of the first test: + +```terminal +AdminLoginTestCest: Admin login test +Signature: Magento\AcceptanceTest\_default\Backend\AdminLoginTestCest:AdminLoginTest +Test: tests/functional/Magento/FunctionalTest/_generated/default/AdminLoginTestCest.php:AdminLoginTest +Scenario -- +I am on page "/admin/admin" +I fill field "#username","admin" +I fill field "#login","123123q" +I click ".actions .action-primary" +I wait for page load 30 +I close admin notification +I see in current url "/admin/admin" +PASSED +``` + +The running test is `AdminLoginTestCest`, which is `Admin login test` (this text is generated from the test name but with the `Cest` part excluded). +Its test signature is `Magento\AcceptanceTest\_default\Backend\AdminLoginTestCest:AdminLoginTest` that matches a `className:methodName` format using namespaces. +A path to the corresponding `Test` is `tests/functional/Magento/FunctionalTest/_generated/default/AdminLoginTestCest.php:AdminLoginTest` (relative to the `acceptance/` directory). + +`Scenario` lists the tests steps as they run during test execution, ending with the successful test verdict `PASSED`. +It means that all test steps were processed as expected. + +#### Failed tests + +The second test fails with the following report: + +```terminal +AdminMenuNavigationWithSecretKeysTestCest: Admin menu navigation with secret keys test +Signature: Magento\AcceptanceTest\_default\Backend\AdminMenuNavigationWithSecretKeysTestCest:AdminMenuNavigationWithSecretKeysTest +Test: tests/functional/Magento/FunctionalTest/_generated/default/AdminMenuNavigationWithSecretKeysTestCest.php:AdminMenuNavigationWithSecretKeysTest +Scenario -- +I magento cli "config:set admin/security/use_form_key 1" +Value was saved. +I magento cli "cache:clean config full_page" +Cleaned cache types: +config +full_page +I am on page "/admin/admin" +I wait for page load +I fill field "#username","admin" +I fill field "#login","123123q" +I click ".actions .action-primary" +I wait for page load 30 +I close admin notification +I click "//li[@id='menu-magento-backend-stores']" +I wait for loading mask to disappear +I click "#nav li[data-ui-id='menu-magento-config-system-config']" +I wait for page load +I see current url matches "~\/admin\/system_config\/~" +I see "#something" +I save screenshot +FAIL + +I magento cli "config:set admin/security/use_form_key 0" +Value was saved. +I magento cli "cache:clean config full_page" +Cleaned cache types: +config +full_page +I am on page "/admin/admin/auth/logout/" +-------------------------------------------------------------------------------- +``` + +The general test details and scenario has the same format as in the Passed test. +The interesting part starts near the `FAIL` line. + +```terminal +I see "#something" +I save screenshot +FAIL +``` + +When a test step fails, the MFTF always saves a screenshot of the web page with the failing state immediately after the failure occurs. +`I save screenshot` follows the failing test step `I see "#something"` in our case. + +A screenshot of the fail goes at the `acceptance/tests/_output` directory in both PNG and HTML formats: + +- `Magento.AcceptanceTest._default.Backend.AdminMenuNavigationWithSecretKeysTestCest.AdminMenuNavigationWithSecretKeysTest.fail.html` +- `Magento.AcceptanceTest._default.Backend.AdminMenuNavigationWithSecretKeysTestCest.AdminMenuNavigationWithSecretKeysTest.fail.png` + +The file name encodes: + +- `Magento` namespace +- with the `AcceptanceTest` test type +- generated as a part of the `_default` suite +- defined at the `Magento_Backend` module +- implemented in the `AdminMenuNavigationWithSecretKeysTestCest` PHP class +- with the `AdminMenuNavigationWithSecretKeysTest` test name +- and execution status `fail` + +Actions after `FAIL` are run as a part of the [`after`][] hook of the test. + +### Test result report + +After the MFTF completed test execution, it generates a general report about test results along with detailed information about each fail. + +```terminal +-------------------------------------------------------------------------------- +DEPRECATION: Calling the "Symfony\Component\BrowserKit\Client::getInternalResponse()" method before the "request()" one is deprecated since Symfony 4.1 and will throw an exception in 5.0. /Users/.../magento2ce/vendor/symfony/browser-kit/Client.php:208 + +Time: 52.43 seconds, Memory: 16.00MB + +There was 1 failure: +--------- +``` + +First you see warnings and deprecations. +The `DEPRECATION` here is thrown by an MFTF dependency (Symfony) that is out of the scope for test writers and should be considered by MFTF contributors. +If you encounter this type of reporting, [report an issue][]. + +Then, the MFTF reports that the test run took 52.43 seconds using 16 MB of system RAM. +And, finally, that there was `1 failure`. + +Next, the report provides details about the test failure. + +```terminal +--------- +1) AdminMenuNavigationWithSecretKeysTestCest: Admin menu navigation with secret keys test +Test tests/functional/Magento/FunctionalTest/_generated/default/AdminMenuNavigationWithSecretKeysTestCest.php:AdminMenuNavigationWithSecretKeysTest +Step See "#something" +Fail Failed asserting that on page /admin/admin/system_config/index/key/678b7ba922c.../ +--> DASHBOARD +SALES +CATALOG +CUSTOMERS +MARKETING +CONTENT +REPORTS +STORES +SYSTEM +FIND PARTNERS & EXTENSIONS +Configuration +admin +1 +Store View: Default Config +What is this? +Save Config +Country Options +State Options +Locale Options +Store Information +Store Name +Store Phone Number +Store Hours of Operation +Countr +[Content too long to display. See complete response in '/Users/dmytroshevtsov/Projects/vagrant/vagrant-magento/magento2ce/dev/tests/acceptance/tests/_output/' directory] +--> contains "#something". + +Scenario Steps: + +23. $I->amOnPage("/admin/admin/auth/logout/") at tests/functional/Magento/FunctionalTest/_generated/default/AdminMenuNavigationWithSecretKeysTestCest.php:54 +22. // Cleaned cache types: +config +full_page +21. $I->magentoCLI("cache:clean config full_page") at tests/functional/Magento/FunctionalTest/_generated/default/AdminMenuNavigationWithSecretKeysTestCest.php:52 +20. // Value was saved. +19. $I->magentoCLI("config:set admin/security/use_form_key 0") at tests/functional/Magento/FunctionalTest/_generated/default/AdminMenuNavigationWithSecretKeysTestCest.php:50 +18. $I->saveScreenshot() at tests/functional/Magento/FunctionalTest/_generated/default/AdminMenuNavigationWithSecretKeysTestCest.php:63 +``` + +- `1) AdminMenuNavigationWithSecretKeysTestCest: Admin menu navigation with secret keys test` - the failed Codeception test is *AdminMenuNavigationWithSecretKeysTestCest*. It references to the PHP class that implemented the failed test. + +- `Test tests/functional/Magento/FunctionalTest/_generated/default/AdminMenuNavigationWithSecretKeysTestCest.php:AdminMenuNavigationWithSecretKeysTest` - the test is implemented in the *AdminMenuNavigationWithSecretKeysTest* test method of the *tests/functional/Magento/FunctionalTest/_generated/default/AdminMenuNavigationWithSecretKeysTestCest.php* file under `<magento root>/dev/tests/acceptance/`. + It matches the corresponding test defined in XML that is *AdminMenuNavigationWithSecretKeysTest* defined in `<test name="AdminMenuNavigationWithSecretKeysTest">...</test>` + +- `Step See "#something"` - the failing test step is the *see* action with the *#something* selector. It would correspond the `<see selector="#something" ... />` test step in the XML defined tests. + +- `Fail Failed asserting that on page /admin/admin/system_config/index/key/678b7ba922c.../` - the fail occurred on the web page `<MAGENTO_BASE_URL>/admin/admin/system_config/index/key/678b7ba922c.../`. + +```terminal +--> ... +[Content too long to display. See complete response in '/../../magento2/dev/tests/acceptance/tests/_output/' directory] +--> contains "#something". +``` + +The web page is too long to be reported in the CLI, and it is stored at *'/../../magento2/dev/tests/acceptance/tests/_output/'*. +Search the web page by test name *AdminMenuNavigationWithSecretKeysTest*. +The failing test assertion is that the web page contains *contains* a CSS locator *#something*. + +Finally, the report finishes with fairly self-descriptive lines. + +```terminal +FAILURES! +Tests: 2, Assertions: 3, Failures: 1. +``` + +The MFTF encountered failures due to the last test run, that included *2* tests with *3* assertions. +*1* assertion fails. + +## Allure + +Each time you run tests, the MFTF appends an XML file with results at the `tests/_output/allure-results/` directory. + +The official [Allure Test Report][] documentation is well-covered, so we'll list only the CLI commands that you would need for your day-to-day work. + +{: .bs-callout .bs-callout-info } +The following commands are relative to the Magento installation directory. + +To generate a report to the `allure-report/` at the current directory: + +```bash +allure generate dev/tests/acceptance/tests/_output/allure-result +``` + +To generate a report to a particular directory, use the `-o` option: + +```bash +allure generate dev/tests/acceptance/tests/_output/allure-result -o dev/tests/acceptance/tests/_output/allure-report +``` + +To launch the generated report in a web browser: + +```bash +allure open dev/tests/acceptance/tests/_output/allure-report +``` + +{% include note.html +type='info' +content=' +By default, Allure generates reports in the `allure-report/` at the current directory. +For example, if you run the command without `-o` flag while you are in the `magento2/` directory, Allure will generate a report at the `magento2/allure-report/` directory. + +```bash +allure generate dev/tests/acceptance/tests/_output/allure-result +``` + +Example of file structure after the command run: + +```terminal +magento2 +├── allure-report +├── app +├── bin +├── dev +├── ... +``` + +And if you run the `open` command with no arguments while you are in the same directory (`magento2/`): + +```bash +allure open +``` + +Allure would attempt to open a generated report at the `magento2/allure-report/` directory.' +%} + +To clean up existing reports before generation (for example after getting new results), use the `--clean` flag: + +```bash +allure generate dev/tests/acceptance/tests/_output/allure-result --clean +``` + +To generate the HTML Allure report in a temporary folder and open the report in your default web browser: + +```bash +allure serve dev/tests/acceptance/tests/_output/allure-results/ +``` + +Refer to the [Reporting section][] for more Allure CLI details. + +<!-- Link definitions --> + +[`after`]: test.html#after-tag +[`run:group`]: commands/mftf.html#rungroup +[`run:test`]: commands/mftf.html#runtest +[Allure Framework]: https://docs.qameta.io/allure/ +[Allure Test Report]: http://allure.qatools.ru/ +[codecept]: commands/codeception.html +[codeception]: https://codeception.com/docs/reference/Commands +[mftf]: commands/mftf.html +[report an issue]: https://github.com/magento/magento2-functional-testing-framework/blob/master/.github/CONTRIBUTING.md#report-an-issue +[Reporting section]: https://docs.qameta.io/allure/#_reporting diff --git a/docs/section.md b/docs/section.md new file mode 100644 index 000000000..a8c716cb1 --- /dev/null +++ b/docs/section.md @@ -0,0 +1,151 @@ +--- +mftf-release: 2.1.2 +redirect_from: /guides/v2.3/magento-functional-testing-framework/2.3/section.html +--- + +# Section structure + +_This topic was updated due to the {{page.mftf-release}} MFTF release._ +{: style="text-align: right"} + +{%raw%} +A `<section>` is a reusable part of a [`<page>`](./page.html) and is the standard file for defining UI elements on a page used in a test. + +A `<section>` can define: + +- An explicit element that has a selector equal to the constant string. Example: `selector="#add_root_category_button"` +- A parameterized element that contains substitutable values in the selector. Example: `selector="#element .{{var1}} .{{var2}}"`. + +{% endraw %} + +## Substitutable values + +Substitutable values in the test can be of the following formats: + +- String literals (`stringLiteral`) +- References to a [data entity](./data.html) (XML data from the corresponding `.../Data/*.xml`) such as `entityName.Field`. +- Persisted data: + - `$persistedCreateDataKey.field$` for data created in the scope of a [test](./test.html#test-tag) using the [`<createData>`](./test/actions.html#createdata) action with `stepKey="persistedCreateDataKey"`. + - `$$persistedCreateDataKey.field$$` for data created in [before](./test.html#before-tag) and [after](./test.html#after-tag) hooks. Even though `<before>`and `<after>` are nested inside a [test](./test.html#test-tag), persisted data is stored differently when it is done in a test hook. Therefore it must be accessed with a different notation. + +The following diagram shows the XML structure of an MFTF section: + +{%include_relative img/section-dia.svg%} + +## Format + +The format of a `<section>` is: + +```xml +<?xml version="1.0" encoding="UTF-8"?> + +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name=""> + <element name="" type="" selector="" /> + <element name="" type="" selector="" parameterized="true"/> + <element name="" type="" selector="" timeout=""/> + </section> +</sections> +``` + +## Principles + +The following conventions apply to MFTF sections: + +- `<section>` name must be alphanumeric. +- `*Section.xml` is stored in the _Section_ directory of a module. +- The name format is `{Admin|Storefront}{SectionDescription}Section.xml`. +- Camel case is used for `<section>` elements. + They describe the function of the element rather than attempting to describe the selector used. + +## Example + +Example (`.../Catalog/Section/AdminCategorySidebarActionSection.xml` file): + +```xml +<?xml version="1.0" encoding="utf-8"?> + +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="AdminCategorySidebarActionSection"> + <element name="addRootCategoryButton" type="button" selector="#add_root_category_button" timeout="30"/> + <element name="addSubcategoryButton" type="button" selector="#add_subcategory_button" timeout="30"/> + </section> +</sections> +``` + +This example uses a `AdminCategorySidebarActionSection` section. All sections with same name will be merged during test generation. + +The `AdminCategorySidebarActionSection` section declares two buttons: + +- `addRootCategoryButton` - button with a `#add_root_category_button` locator on the parent web page +- `addSubcategoryButton` - button with a `#add_subcategory_button` locator on the parent web page + +The following is an example of a call in test: +{%raw%} + +```xml +<!-- Click on the button with locator "#add_subcategory_button" on the web page--> +<click selector="{{AdminCategorySidebarActionSection.addSubcategoryButton}}" stepKey="clickOnAddSubCategory"/> +``` + +## Elements reference + +### section {#section-tag} + +`<section>` contains the sequence of UI elements in a section of a [page](./page.html). + +Attributes|Type|Use|Description +---|---|---|--- +`name`|string|required|Unique section name identifier. +`remove`|boolean|optional|The default is `false`. Set to `true` to remove this element during parsing. + +### element {#element-tag} + +`<element>`is a UI element used in an [action](./test/actions.html). + +Attributes|Type|Use|Description +---|---|---|--- +`name`|string|required|The element name; Must be alphanumeric. +`type`|string|required|The type of the element. Possible values: `text`, `textarea`, `input`, `button`, `checkbox`, `radio`, `checkboxset`, `radioset`, `date`, `file`, `select`, `multiselect`, `wysiwyg`, `iframe`, `block`. +`selector`|string|optional|[XPath](https://www.w3schools.com/xml/xpath_nodes.asp) or [CSS](https://www.w3schools.com/cssref/css_selectors.asp) selector of the element. +`locatorFunction`|string|optional|[Locator function](./section/locator-functions.html) declaration to be used in lieu of a selector. +`timeout`|string|optional|The timeout after interaction with the element (in seconds). The default is _none_. +`parameterized`|boolean|optional|Include and set to `true` if the `selector` for this element has parameters that need to be replaced for proper use. Learn more in [Parameterized selectors](./section/parameterized-selectors.html). +`remove`|boolean|optional|The default is `false`. Set to `true` to remove this element during parsing. + +#### `timeout` attribute {#timeout-attribute} + +The attribute adds the [waitForPageLoad] action after a reference to the element in test. +The most usual use case is a test step with a button click action. + +**Use case**: Set a timeout of 30 seconds after clicking the `signIn` button. + +The section element code declaration containing the timeout attribute: + +> StorefrontSigninSection.xml + +```xml +... +<element name="signIn" type="button" selector="#signIn" timeout="30"/> +... +``` + +The test step that covers the use case: + +> StorefrontSigninTest.xml + +```xml +... +<click selector="{{StorefrontSigninSection.signIn}}" ../> +... +``` + +Whenever the `signIn` button is used in a test, the MFTF will add a 30 second `waitForPageLoad` action immediately after the `click`. + +{% endraw %} + +<!-- Link definitions --> + +[waitForPageLoad]: test/actions.html#waitforpageload \ No newline at end of file diff --git a/docs/section/locator-functions.md b/docs/section/locator-functions.md new file mode 100644 index 000000000..9962c6f24 --- /dev/null +++ b/docs/section/locator-functions.md @@ -0,0 +1,48 @@ +--- +mftf-release: 2.0.2 +redirect_from: /guides/v2.3/magento-functional-testing-framework/2.3/section/locator-functions.html +--- + +# Locator functions + +_This topic was updated due to the {{page.mftf-release}} MFTF release._ +{: style="text-align: right"} + +{%raw%} + +## Define Locator::functions in elements + + Codeception has a set of very useful [Locator functions](http://codeception.com/docs/reference/Locator) that may be used by elements inside a [section](../section.html). + +Declare an element with a `locatorFunction`: + +```xml +<element name="simpleLocator" type="button" locatorFunction="Locator::contains('label', 'Name')"/> +``` + +When using the `locatorFunction`, omit `Locator::` for code simplicity: + +```xml +<element name="simpleLocatorShorthand" type="button" locatorFunction="contains('label', 'Name')"/> +``` + +An element's `locatorFunction` can also be parameterized the same way as [parameterized selectors](./parameterized-selectors.html): + +```xml +<element name="simpleLocatorTwoParam" type="button" locatorFunction="contains({{arg1}}, {{arg2}})" parameterized="true"/> +``` + +An element cannot, however, have both a `selector` and a `locatorFunction`. + +## Call Elements that use locatorFunction + +Given the above element definitions, you call the elements in a test just like any other element. No special reference is required, as you are still just referring to an `element` inside a `section`: + +```xml +<test name="LocatorFuctionTest"> + <click selector="{{LocatorFunctionSection.simpleLocator}}" stepKey="SimpleLocator"/> + <click selector="{{LocatorFunctionSection.simpleLocatorTwoParam('string1', 'string2')}}" stepKey="TwoParamLiteral"/> +</test> +``` + +{%endraw%} \ No newline at end of file diff --git a/docs/section/parameterized-selectors.md b/docs/section/parameterized-selectors.md new file mode 100644 index 000000000..4051afdac --- /dev/null +++ b/docs/section/parameterized-selectors.md @@ -0,0 +1,160 @@ +--- +mftf-release: 2.0.2 +redirect_from: /guides/v2.3/magento-functional-testing-framework/2.3/section/parameterized-selectors.html +--- + +# Parameterized selectors + +_This topic was updated due to the {{page.mftf-release}} MFTF release._ +{: style="text-align: right"} + +{%raw%} +Use the following examples to create and use parameterized selectors in the MFTF. + +## Set up a selector in section + +Create a new `<element/>` in a `<section></section>`, : + +```xml +<section name="SampleSection"> + <element name="" type="" selector=""/> +</section> +``` + +Add the attribute `parameterized="true"` to the `<element/>`: + +```xml +<section name="SampleSection"> + <element name="" type="" selector="" parameterized="true"/> +</section> +``` + +Add your selector in the `selector=""` attribute: + +```xml +<section name="SampleSection"> + <element name="" type="" selector="#element" parameterized="true"/> +</section> +``` + +### Selector with single variable + +For the parameterized part of the selector, add `{{var1}}` to represent the first piece of data that you want to replace: + +```xml +<section name="SampleSection"> + <element name="" type="" selector="#element .{{var1}}" parameterized="true"/> +</section> +``` + +Add a descriptive name in the `name=""` attribute: + +```xml +<section name="SampleSection"> + <element name="oneParamElement" type="" selector="#element .{{var1}}" parameterized="true"/> +</section> +``` + +Add the type of UI element that the `<element/>` represents in `type`: + +```xml +<section name="SampleSection"> + <element name="oneParamElement" type="text" selector="#element .{{var1}}" parameterized="true"/> +</section> +``` + +### Selector with multiple variables + +For the parameterized part of the selector, add `{{var1}}, {{var2}}, ..., {{varN}}` for each parameter that you need to pass in: + +```xml +<section name="SampleSection"> + <element name="threeParamElement" type="text" selector="#element .{{var1}} .{{var2}}" parameterized="true"/> +</section> +``` + +```xml +<section name="SampleSection"> + <element name="threeParamElement" type="text" selector="#element .{{var1}} .{{var2}}-{{var3}}" parameterized="true"/> +</section> +``` + +{: .bs-callout .bs-callout-info } +There is no need to use sequential variables like `{{var1}}`, `{{var2}}`. Parameterized replacement reads variables and maps them to the test call of the element sequentially from left to right, meaning you can use a selector like `#element .{{categoryId}} .{{productId}}`." + +## Use a parameterized selector in a test + +Create a new [test](../test.html): + +```xml +<test name="SampleTest"> + +</test> +``` + +Add an action: + +```xml +<test name="SampleTest"> + <click selector="" stepKey="click1"/> +</test> +``` + +Enter `"{{}}"` in the `selector=""` attribute: + +```xml +<test name="SampleTest"> + <click selector="{{}}" stepKey="click1"/> +</test> +``` + +Make a reference to the section that the element is assigned to inside the `{{}}`: + +```xml +<test name="SampleTest"> + <click selector="{{SampleSection}}" stepKey="click1"/> +</test> +``` + +Add name of a parameterized element, separated by `"."`, inside the `{{}}`: + +```xml +<test name="SampleTest"> + <click selector="{{SampleSection.threeParamElement}}" stepKey="click1"/> +</test> +``` + +Add a set of `"()"` following the parameterized element inside the `{{}}`: + +```xml +<test name="SampleTest"> + <click selector="{{SampleSection.threeParamElement()}}" stepKey="click1"/> +</test> +``` + +Add the first parameter, that you would like to pass to the selector, inside of the `()`: + +```xml +<test name="SampleTest"> + <click selector="{{SampleSection.threeParamElement(_defaultCategory.is_active)}}" stepKey="click1"/> +</test> +``` + +Add the second or third parameters, that you'd like to pass to the selector, separated by `, `: + +```xml +<test name="SampleTest"> + <click selector="{{SampleSection.threeParamElement(_defaultCategory.is_active,'StringLiteral',$createDataKey.id$)}}" stepKey="click1"/> +</test> +``` + +Any data can be used in parameterized elements, as well as entered in test actions: + +* `_defaultCategory.is_active` is a reference to `<data key="is_active">` in `<entity name="_defaultCategory" ... ></entity>` in the corresponding `.../Data/*.xml`. +* `'StringLiteral'` is a literal. +* `$createDataKey.id$` is a reference to persisted data created in the `SampleTest1` within the `stepKey="createDataKey"` action. +* `{$variable}` is a reference to data returned by a test action, like `<grabValueFrom>`. + +{%endraw%} + + diff --git a/docs/suite.md b/docs/suite.md new file mode 100644 index 000000000..543bb05ac --- /dev/null +++ b/docs/suite.md @@ -0,0 +1,305 @@ +--- +mftf-release: 2.3.9 +redirect_from: /guides/v2.3/magento-functional-testing-framework/2.3/suite.html +--- + +# Suites + +_This topic was updated due to the {{page.mftf-release}} MFTF release._ +{: style="text-align: right"} + +Suites are essentially groups of tests that run in the specific conditions (preconditions and postconditions). +They enable you including, excluding, and grouping tests for a customized test run when you need it. +You can form suites using separate tests, groups, and modules. + +Each suite must be defined in the `<magento 2 root>/dev/tests/acceptance/tests/_suite/suite.xml` file. +The generated tests for each suite go into a separate directory under `<magento 2 root>/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/_generated/`. +By default, all generated tests are stored in the _default_ suite under `.../Magento/FunctionalTest/_generated/default/` + +{% +include note.html +type="info" +content="If a test is generated into at least one custom suite, it will not appear in the _default_ suite." +%} + +## Format + +The format of a suite: + +```xml +<?xml version="1.0" encoding="UTF-8"?> + +<suites xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Suite/etc/suiteSchema.xsd"> + <suite name=""> + <before> + </before> + <after> + </after> + <include> + <test name=""/> + <group name=""/> + <module name="" file=""/> + </include> + <exclude> + <test name=""/> + <group name=""/> + <module name="" file=""/> + </exclude> + </suite> +</suites> +``` + +## Principles + +- A suite name: + - must not match any existing group value. + For example, the suite `<suite name="ExampleTest">` will fail during test run if any test contains in annotations `<group value="ExampleTest">`. + - must not be `default` or `skip`. Tests that are not in any suite are generated under the `default` suite. + The suite name `skip` is synonymous to including a test in the `<group value="skip"/>`, which will be deprecated in MFTF 3.0.0. + - can contain letters, numbers, and underscores. + - should be upper camel case. +- A suite must contain at least one `<include>`, or one `<exclude>`, or both. +- Using `<before>` in a suite, you must add the corresponding `<after>` to restore the initial state of your testing instance. + +## Conditions + +Using suites enables test writers to consolidate conditions that are shared between tests. +The code lives in one place and executes once per suite. + +- Set up preconditions and postconditions using [actions] in [`<before>`] and [`<after>`] correspondingly, just similar to use in a [test]. +- Clean up after suites just like after tests. + The MFTF enforces the presence of both `<before>` and `<after>` if either is present. +- Do not reference in the subsequent tests to data that was persisted in the preconditions. + Referencing to `$stepKey.field$` of these actions is not valid. + +## Test writing + +Since suites enable precondition consolidation, a common workflow for test writing is adding a new test to an existing suite. +Such test is generated in context of the suite that contains it. +You cannot isolate this test from preconditions of the suite; it cannot be used outside of the suite at the same time. + +There are several ways to generate and execute your new test in the context of a suite: + +- Edit the appropriate `suite.xml` to include your test only and run: + + ```bash + vendor/bin/mftf run:group <suiteName> + ``` + +- Temporarily add a group to your test like `<group value="foo">` and run: + + ```bash + vendor/bin/mftf run:group foo + ``` + +- To limit generation to your suite/test combination, run in conjunction with the above: + + ```bash + vendor/bin/mftf generate:suite <suite> + ``` + +- To generate any combination of suites and tests, use [`generate:tests`] with the `--tests` flag. + +## Examples + +### Enabling/disabling WYSIWYG in suite conditions + +<!-- {{raw}} --> + +```xml +<suites xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Suite/etc/suiteSchema.xsd"> + <suite name="WYSIWYG"> + <before> + <actionGroup ref="LoginAsAdmin" stepKey="login"/> + <amOnPage url="admin/admin/system_config/edit/section/cms/" stepKey="navigateToConfigurationPage" /> + <waitForPageLoad stepKey="wait1"/> + <conditionalClick stepKey="expandWYSIWYGOptions" selector="{{ContentManagementSection.WYSIWYGOptions}}" dependentSelector="{{ContentManagementSection.CheckIfTabExpand}}" visible="true" /> + <waitForElementVisible selector="{{ContentManagementSection.EnableWYSIWYG}}" stepKey="waitForEnableWYSIWYGDropdown1" /> + <waitForElementVisible selector="{{ContentManagementSection.EnableSystemValue}}" stepKey="waitForUseSystemValueVisible"/> + <uncheckOption selector="{{ContentManagementSection.EnableSystemValue}}" stepKey="uncheckUseSystemValue"/> + <selectOption selector="{{ContentManagementSection.EnableWYSIWYG}}" userInput="Enabled by Default" stepKey="selectOption1"/> + <click selector="{{ContentManagementSection.WYSIWYGOptions}}" stepKey="collapseWYSIWYGOptions" /> + <click selector="{{ContentManagementSection.Save}}" stepKey="saveConfig" /> + </before> + <after> + <actionGroup ref="LoginAsAdmin" stepKey="login"/> + <actionGroup ref="DisabledWYSIWYG" stepKey="disable"/> + </after> + <include> + <group name="WYSIWYG"/> + </include> + </suite> +</suites> +``` + +<!-- {{endraw}} --> +This example declares a suite with the name `WYSIWYG`. +The suite enables WYSIWYG *before* running tests. +It performs the following steps: + +1. Log in to the backend. +2. Navigate to the **Configuration** page. +3. Enable **WYSIWYG** in the Magento instance. + +*After* the testing, the suite returns the Magento instance to the initial state disabling WYSIWYG: + +1. Log back in. +2. Disable **WYSIWYG** so that + +This suite includes all tests that contain the `<group value="WYSIWYG"/>` annotation. + +### Execute Magento CLI commands in suite conditions + +```xml +<suites xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Suite/etc/suiteSchema.xsd"> + <suite name="Cache"> + <before> + <magentoCLI stepKey="disableCache" command="cache:disable"/> + </before> + <after> + <magentoCLI stepKey="enableCache" command="cache:enable"/> + </after> + <include> + <test name="SomeCacheRelatedTest"/> + <group name="CacheRelated"/> + </include> + </suite> +</suites> +``` + +This example declares a suite with the name `Cache`. + +Preconditions: + +1. It disables the Magento instance cache entirely before running the included tests. +2. After the testing, it re-enables the cache. + +The suite includes a specific test `SomeCacheRelatedTest` and every `<test>` that includes the `<group value="CacheRelated"/>` annotation. + +### Change Magento configurations in suite conditions + +```xml +<suites xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Suite/etc/suiteSchema.xsd"> + <suite name="PaypalConfiguration"> + <before> + <createData entity="SamplePaypalConfig" stepKey="createSamplePaypalConfig"/> + </before> + <after> + <createData entity="DefaultPayPalConfig" stepKey="restoreDefaultPaypalConfig"/> + </after> + <include> + <module name="Catalog"/> + </include> + <exclude> + <test name="PaypalIncompatibleTest"/> + </exclude> + </suite> +</suites> +``` + +This example declares a suite with the name `PaypalConfiguration`: + +- `<before>` block persists a Paypal Configuration enabling all tests in this suite to run under the newly reconfigured Magento instance. +- `<after>` block deletes the persisted configuration, returning Magento to its initial state. +- The suite includes all tests from the `Catalog` module, except the `PaypalIncompatibleTest` test. + +## Elements reference + +### suites {#suites-tag} + +The root element for suites. + +### suite {#suite-tag} + +A set of "before" and "after" preconditions, and test filters to include and exclude tests in the scope of suite. + +Attributes|Type|Use|Description +---|---|---|--- +`name`|string|required|Unique suite name identifier. +`remove`|boolean|optional|Removing the suite during merging. + +It can contain `<before>`, `<after>`, `<include>`, and `<exclude>`. + +### before {#before-tag} + +A suite hook with preconditions that executes once before the suite tests. + +It may contain test steps with any [actions] and [action groups]. + +{% include note.html +type="warning" +content="Tests in the suite are not run and screenshots are not saved in case of a failure in the before hook. +To troubleshoot the failure, run the suite locally." +%} + +### after {#after-tag} + +A suite hook with postconditions executed once after the suite tests. + +It may contain test steps with any [actions] and [action groups]. + +### include {#include-tag} + +A set of filters that you can use to specify which tests to include in the test suite. + +It may contain filters by: + +- test which names a specific `<test>`. +- group which refers to a declared `group` annotation. +- module which refers to `test` files under a specific Magento Module. + +The element can contain [`<test>`], [`<group>`], and [`<module>`]. + +### exclude {#exclude-tag} + +A set of filters that you can use to specify which tests to exclude in the test suite. + +There two types of behavior: + +1. Applying filters to the included tests when the suite contains [`<include>`] filters. + The MFTF will exclude tests from the previously included set and generate the remaining tests in the suite. +2. Applying filter to all tests when the suite does not contain [`<include>`] filters. + The MFTF will generate all existing tests except the excluded. + In this case, the custom suite will contain all generated tests except excluded, and the _default_ suite will contain the excluded tests only. + +It may contain filters by: + +- test which names a specific `<test>`. +- group which refers to a declared `group` annotation. +- module which refers to `test` files under a specific Magento Module. + +The element may contain [`<test>`], [`<group>`], and [`<module>`]. + +### test {#test-tag} + +Attributes|Type|Use|Description +---|---|---|--- +`name`|string|required|Filtering a test by its name. +`remove`|boolean|optional|Removing the filter during merging. + +### group {#group-tag} + +Attributes|Type|Use|Description +---|---|---|--- +`name`|string|required|Filtering tests by the `<group>` annotation. +`remove`|boolean|optional|Removing the filter during merging. + +### module {#module-tag} + +Attributes|Type|Use|Description +---|---|---|--- +`name`|string|required|Filtering tests by their location in the corresponding module. +`file`|string|optional|Filtering a specific test file in the module. +`remove`|boolean|optional|Removing the filter during merging. + +<!-- Link definitions --> +[actions]: test/actions.html +[action groups]: test/action-groups.html +[`<after>`]: #after-tag +[`<before>`]: #before-tag +[`generate:tests`]: commands/mftf.html#generatetests +[test]: test.html +[`<test>`]: #test-tag +[`<group>`]: #group-tag +[`<module>`]: #module-tag +[`<include>`]: #include-tag \ No newline at end of file diff --git a/docs/test.md b/docs/test.md new file mode 100644 index 000000000..afcc443a0 --- /dev/null +++ b/docs/test.md @@ -0,0 +1,159 @@ +--- +mftf-release: 2.3.0 +redirect_from: /guides/v2.3/magento-functional-testing-framework/2.3/test.html +--- + +# Test + +_This topic was updated due to the {{page.mftf-release}} MFTF release._ +{: style="text-align: right"} + +Test cases in the Magento Functional Testing Framework (MFTF) are defined in XML as [`<tests>`]. +`<tests>` is a [Codeception test container][Codeception] that contains multiple individual tests with test metadata and before and after actions. + +MFTF `<tests>` is considered a sequence of actions with associated parameters. +Any failed [assertion] within a test constitutes a failed test. + +{% +include note.html +type = "info" +content='`<before>` and `<after>` hooks are not global within `<tests>`. +They only apply to the `<test>` in which they are declared. + +The steps in `<after>` are run in both successful **and** failed test runs.' +%} + +The following diagram shows the structure of an MFTF test case: + +{% include_relative img/test-dia.svg %} + +## Format + +The format of `<tests>` is: + +```xml +<?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="" insertBefore="" insertAfter=""> + <annotations> + <!-- TEST ANNOTATIONS --> + </annotations> + <before> + <!-- ACTIONS AND ACTION GROUPS PERFORMED BEFORE THE TEST --> + </before> + <after> + <!-- ACTIONS AND ACTION GROUPS PERFORMED AFTER THE TEST --> + </after> + <!-- TEST ACTIONS, ACTION GROUPS, AND ASSERTIONS--> + </test> +</tests> +``` + +## Principles + +The following conventions apply to MFTF tests: + +* All names within the framework are in the CamelCase format. +* `<test>` name must be alphanumeric. +* Each action and action group has its own identifier `<stepKey>` for reference purposes. +* A test may have any number of [assertions][assertion] at any point within the `<test>`. +* If `<test>` is included in `<suite>`, it **cannot be generated in isolation** to the rest of the contents of the suite (see [suites] for details). + +Multiple `<test>` tags per XML file can make it hard to find and organize tags. +To simplify, we generate one `test.php` file per `<test>` tag provided, though we support both single and multiple `<test>` tags per XML file. + +## Elements reference + +There are several XML elements that are used in `<tests>` in the MFTF. + +### tests {#tests-tag} + +`<tests>` is a container for multiple tests. It is a group of test methods that define test flows within a test case. + +`<tests>` must contain at least one [`<test>`]. + +### test {#test-tag} + +`<test>` is a set of steps, including [actions] and [assertions][assertion]. It is a sequence of test steps that define test flow within a test method. + + +Attribute|Type|Use|Description +---|---|---|--- +`name`|string|optional|The test identifier. +`remove`|boolean|optional|Set `true` to remove the test when merging. +`insertBefore`|string|optional| This option is used for [merging]. It enables you to add all test actions contained in the original test into a test with the same name BEFORE the test step with `stepKey` that you assigned in `insertBefore`. +`insertAfter`|string|optional| Set `stepKey` of the test step after which you want to insert the test when [merging]. +`extends`|string|optional|A name of the parent test to [extend]. + +`<test>` may also contain [`<annotations>`], [`<before>`], [`<after>`], any [action][actions], or [`<actionGroup>`]. + +### annotations {#annotations-tag} + +[Annotations] are supported by both [Codeception] and [Allure]. + +Codeception annotations typically provide metadata and are able to influence test selection. +Allure annotations provide metadata for reporting. + +### before {#before-tag} + +`<before>` wraps the steps to perform before the [`<test>`]. + +`<before>` may contain these child elements: + + * Any [`<action>`][actions] + * [`<actionGroup>`] + +### after {#after-tag} + +`<after>` wraps the steps to perform after the [`<test>`]. +The steps are run in both successful **and** failed test runs. + +`<after>` may contain: + + * Any [`<action>`][actions] + * [`<actionGroup>`] + +### actionGroup {#actiongroup-tag} + +`<actionGroup>` calls a corresponding [action group]. + +Attribute|Type|Use|Description +---|---|---|--- +`ref`|string|required|References the required action group by its `name`. +`stepKey`|string|required| Identifies the element within `<test>`. +`before`|string|optional| `<stepKey>` of an action or action group that must be executed next while merging. +`after`|string|optional| `<stepKey>` of an action or action group that must be executed one step before the current one while merging. + +`<actionGroup>` may contain [`<argument>`]. + +### argument {#argument-tag} + +`<argument>` sets an argument that is used in the parent [`<actionGroup>`]. + +Attribute|Type|Use +---|---|--- +`name`|string|optional| Name of the argument. +`value`|string|optional| Value of the argument. + +See [Action groups][action group] for more information. + +<!-- Link definitions --> + +[`<actionGroup>`]: #actiongroup-tag +[`<after>`]: #after-tag +[`<annotations>`]: #annotations-tag +[`<argument>`]: #argument-tag +[`<before>`]: #before-tag +[`<test>`]: #test-tag +[`<tests>`]: #tests-tag +[action group]: ./test/action-groups.html +[actions]: ./test/actions.html +[Allure]: https://github.com/allure-framework/ +[Annotations]: ./test/annotations.html +[assertion]: ./test/assertions.html +[Codeception]: https://codeception.com/docs/07-AdvancedUsage +[extend]: extending.html +[merging]: ./merging.html#insert-after +[suites]: ./suite.html \ No newline at end of file diff --git a/docs/test/action-groups.md b/docs/test/action-groups.md new file mode 100644 index 000000000..f93e4b594 --- /dev/null +++ b/docs/test/action-groups.md @@ -0,0 +1,270 @@ +--- +mftf-release: 2.3.7 +redirect_from: /guides/v2.3/magento-functional-testing-framework/2.3/test/action-groups.html +--- + +# Action groups + +_This topic was updated due to the {{page.mftf-release}} MFTF release._ +{: style="text-align: right"} + +In the MFTF, you can re-use a group of [actions](./actions.html){:target="_blank"}, such as logging in as an administrator or a customer, declared in an XML file when you need to perform the same sequence of actions multiple times. + +The following diagram shows the structure of an MFTF action group: + +{% include_relative img/action-groups-dia.svg %} + +## Principles + +The following conventions apply to MFTF action groups: + +- All action groups are declared in XML files and stored in the `<module>/ActionGroup/` directory. +- Every file name ends with `ActionGroup`, such as `LoginToAdminActionGroup`. + +The XML format for the `actionGroups` declaration is: + +```xml +<?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=""> + <arguments> + <argument name=""/> + <argument name="" defaultValue=""/> + <argument name="" defaultValue="" type=""/> + </arguments> + </actionGroup> +</actionGroups> +``` + +## Example + +{% raw %} + +These examples build a declaration for a group of actions that grant authorization to the Admin area, and use the declaration in a test. + +The _Backend/ActionGroup/LoginToAdminActionGroup.xml_ `<actionGroup>` relates to the functionality of the _Backend_ module. +In [test](../test.html), the name and identifier of the `<actionGroup>` is used as a reference in the `ref` parameter, such as `ref="LoginToAdminActionGroup"`. + +### Create an action group declaration + +To create the `<actionGroup>` declaration: + +1. Begin with a _Backend/ActionGroup/LoginToAdminActionGroup.xml_ template for the `<actionGroup>`: + + ```xml + <?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="LoginToAdminActionGroup"> + ... + </actionGroup> + </actionGroups> + ``` + +1. Add actions to the `actionGroup` arguments: + + ```xml + <actionGroup name="LoginToAdminActionGroup"> + <fillField stepKey="fillUsername" selector="#username" userInput="{{adminUser.username}}" /> + <fillField stepKey="fillPassword" selector="#password" userInput="{{adminUser.password}}" /> + <click stepKey="click" selector="#login" /> + </actionGroup> + ``` + +1. The `userInput` variable must contain a data value for test. + Add a default data value for the variable to use in the most common cases. + For this example, the default value is `_defaultAdmin`. + + ```xml + <argument name="adminUser" defaultValue="_defaultAdmin"/> + ``` + +1. The following example shows the complete declaration: + + ```xml + <?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="LoginToAdminActionGroup"> + <arguments> + <argument name="adminUser" defaultValue="_defaultAdmin"/> + </arguments> + <fillField stepKey="fillUsername" selector="#username" userInput="{{adminUser.username}}" /> + <fillField stepKey="fillPassword" selector="#password" userInput="{{adminUser.password}}" /> + <click stepKey="click" selector="#login" /> + </actionGroup> + </actionGroups> + ``` + +### Use the declaration in a test + +In this test example, we want to add the following set of actions: + +```xml +<fillField stepKey="fillUsername" selector="#username" userInput="{{CustomAdminUser.username}}" /> +<fillField stepKey="fillPassword" selector="#password" userInput="{{CustomAdminUser.password}}" /> +<click stepKey="click" selector="#login" /> +``` + +Instead of adding this set of actions, use the _LoginToAdminActionGroup_ `<actionGroup>` declaration in tests: + +1. Reference the `LoginToAdminActionGroup` action group: + + ```xml + <actionGroup stepKey="loginToAdminPanel" ref="LoginToAdminActionGroup"/> + ``` + +1. Update the argument name/value pair to `adminUser` and `CustomAdminUser`: + + ```xml + <actionGroup stepKey="loginToAdminPanel" ref="LoginToAdminActionGroup"> + <argument name="adminUser" value="CustomAdminUser"/> + </actionGroup> + ``` + +## Data type usage + +By default, an [`argument`](#argument-tag) expects an entire `entity` when the `type` value is not defined. +There are cases when you use a string instead of a whole entity. + +For example, the following defines the replacement argument `relevantString` using a primitive data type: + +```xml +<actionGroup name="fillExample"> + <arguments> + <argument name="relevantString" defaultValue="defaultString" type="string"/> + </arguments> + <fillField stepKey="fillField1" selector="#input" userInput="{{relevantString}}"/> + <click stepKey="clickSave" selector="#save"/> + <see stepKey="seeItWorked" selector="#outputArea" userInput="{{relevantString}}"/> + <click stepKey="clickParameterizedSelector" selector="{{SomeSection.parameterizedElement(relevantString)}}"/> +</actionGroup> +``` + +The `string` argument type provides a method to pass a single piece of data to the `<actionGroup>`during a test instead of passing an entire entity. + +### Explicitly define the argument value + +```xml +<actionGroup stepKey="fillWithStringLiteral" ref="fillExample"> + <argument name="relevantString" value="overrideString"/> +</actionGroup> +``` + +### Use persisted data references to define the argument value + +```xml +<actionGroup stepKey="fillWithStringLiteral" ref="fillExample"> + <argument name="relevantString" value="$persistedData.field1$"/> +</actionGroup> +``` + +The `relevantString` argument value points to the data [created](../data.html#persist-data){:target="_blank"} in the `stepKey="persistedData"` test step. +`field1` is a data key of the required data string. +Even with the `persistedData` data entity, the MFTF interprets the `$persistedData.field1$` value as a string. + +### Define the argument value based on data entity resolution + +The argument value points to a piece of data defined in a `data.xml` file. +The `field1` data contains the required string. +MFTF resolves `{{myCustomEntity.field1}}` the same as it would in a `selector` or `userInput` attribute. + +```xml +<actionGroup stepKey="fillWithXmlData" ref="fillExample"> + <argument name="relevantString" value="{{myCustomEntity.field1}}"/> +</actionGroup> +``` + +## Optimizing action group structures + +Structuring properly an action group increases code reusability and readability. + +Starting with an action group such as: + +```xml +<actionGroup name="CreateCategory"> + <arguments> + <argument name="categoryEntity" defaultValue="_defaultCategory"/> + </arguments> + <seeInCurrentUrl url="{{AdminCategoryPage.url}}" stepKey="seeOnCategoryPage"/> + <click selector="{{AdminCategorySidebarActionSection.AddSubcategoryButton}}" stepKey="clickOnAddSubCategory"/> + <see selector="{{AdminHeaderSection.pageTitle}}" userInput="New Category" stepKey="seeCategoryPageTitle"/> + <fillField selector="{{AdminCategoryBasicFieldSection.CategoryNameInput}}" userInput="{{categoryEntity.name}}" stepKey="enterCategoryName"/> + <click selector="{{AdminCategorySEOSection.SectionHeader}}" stepKey="openSEO"/> + <fillField selector="{{AdminCategorySEOSection.UrlKeyInput}}" userInput="{{categoryEntity.name_lwr}}" stepKey="enterURLKey"/> + <click selector="{{AdminCategoryMainActionsSection.SaveButton}}" stepKey="saveCategory"/> + <seeElement selector="{{AdminCategoryMessagesSection.SuccessMessage}}" stepKey="assertSuccess"/> + <seeInTitle userInput="{{categoryEntity.name}}" stepKey="seeNewCategoryPageTitle"/> + <seeElement selector="{{AdminCategorySidebarTreeSection.categoryInTree(categoryEntity.name)}}" stepKey="seeCategoryInTree"/> +</actionGroup> +``` + +{: .no-copy} + +It can be reworked into more manageable pieces, as below. These smaller steps are easier to read, update, and reuse. + +```xml +<actionGroup name="GoToCategoryGridAndAddNewCategory"> + <seeInCurrentUrl url="{{AdminCategoryPage.url}}" stepKey="seeOnCategoryPage"/> + <click selector="{{AdminCategorySidebarActionSection.AddSubcategoryButton}}" stepKey="clickOnAddSubCategory"/> + <see selector="{{AdminHeaderSection.pageTitle}}" userInput="New Category" stepKey="seeCategoryPageTitle"/> +</actionGroup> + +<actionGroup name="FillInBasicCategoryFields"> + <arguments> + <argument name="categoryEntity" defaultValue="_defaultCategory"/> + </arguments> + <fillField selector="{{AdminCategoryBasicFieldSection.CategoryNameInput}}" userInput="{{categoryEntity.name}}" stepKey="enterCategoryName"/> + <click selector="{{AdminCategorySEOSection.SectionHeader}}" stepKey="openSEO"/> + <fillField selector="{{AdminCategorySEOSection.UrlKeyInput}}" userInput="{{categoryEntity.name_lwr}}" stepKey="enterURLKey"/> +</actionGroup> + +<actionGroup name="SaveAndVerifyCategoryCreation"> + <click selector="{{AdminCategoryMainActionsSection.SaveButton}}" stepKey="saveCategory"/> + <seeElement selector="{{AdminCategoryMessagesSection.SuccessMessage}}" stepKey="assertSuccess"/> + <seeInTitle userInput="{{categoryEntity.name}}" stepKey="seeNewCategoryPageTitle"/> + <seeElement selector="{{AdminCategorySidebarTreeSection.categoryInTree(categoryEntity.name)}}" stepKey="seeCategoryInTree"/> +</actionGroup> +``` + +{: .no-copy} + +## Elements reference + +### actionGroups {#actiongroups-tag} + +The `<actionGroups>` element is a root element that contains XML configuration attributes. + +Attribute|Value|Description +---|---|--- +`xmlns:xsi`|`"http://www.w3.org/2001/XMLSchema-instance"`|Tells the XML parser to validate this document against a schema. +`xsi:noNamespaceSchemaLocation`|`"urn:magento:mftf:Test/etc/actionGroupSchema.xsd"`|Relative path to the corresponding schema. + +It may contain one or more `<actionGroup>`. + +### actionGroup {#actiongroup-tag} + +Attribute|Type|Use|Description +---|---|---|--- +`name`|string|required|Identifier of the action group. +`extends`|string|optional|Identifies the action group to extend. + +It may contain `<arguments>`. + +### arguments {#arguments-tag} + +The `<arguments>` element is a wrapper for an array of `<argument>` elements. + +### argument {#argument-tag} + +Attribute|Type|Use|Description +---|---|---|--- +`name`|string|required|Identifier of an argument in the scope of the corresponding action group. +`defaultValue`|string|optional|Provides a default data value. +`type`|Possible values: `string`, `entity` (default).|optional|Defines the argument data type; Defaults to `entity`. + +{% endraw %} diff --git a/docs/test/actions.md b/docs/test/actions.md new file mode 100644 index 000000000..4c4525b0d --- /dev/null +++ b/docs/test/actions.md @@ -0,0 +1,2590 @@ +--- +mftf-release: 2.3.7 +redirect_from: /guides/v2.3/magento-functional-testing-framework/2.3/test/actions.html +--- + +# Test actions + +_This topic was updated due to the {{page.mftf-release}} MFTF release._ +{: style="text-align: right"} + +Actions in the MFTF allow you to automate different scenarios of Magento user's actions. +They are mostly XML implementations of [Codeception actions](http://codeception.com/docs/modules/WebDriver#Actions). +Some actions drive browser elements, while others use REST APIs. + +## Common attributes + +All `<actions>` contain the following attributes that are useful for merging needs. + +### `stepKey` + +`stepKey` is a required attribute that stores a unique identifier of the action. + +Example test step of the `myAction` action with the `conditionalClickStep1` identifier: + +```xml +<myAction stepKey="conditionalClickStep1"/> +``` + +This step can be referenced within the test using `conditionalClickStep1`. + +The value format should met the following principles: + +* Must be unique within [`<test>`](../test.html#test-tag). +* Naming should be as descriptive as possible: + * Describe the action performed. + * Briefly describe the purpose. + * Describe which data is in use. +* Should be in camelCase with lowercase first letter. +* Should be the last attribute of an element. + +### `skipReadiness` + +`skipReadiness` is an optional flag to skip the readiness check. + +Example test step with skipping the readiness check: + +```xml +<myAction skipReadiness="true" stepKey=""/> +``` + +The flag: + +* cannot be used within action groups. + * Can be used on individual actions inside the action group. + +### `before` and `after` + +`before` and `after` are optional attributes that insert the action into the test while merging. The action will be executed before or after the one set in these attributes. The value here is the `stepKey` of reference action. + +Example with `before`: + +```xml +<myAction before="fillField" stepKey="conditionalClickStep1"/> +``` + +`myAction` will be executed before the action, which has `stepKey="fillField"`. + +Example with `after`: + +```xml +<myAction after="fillField" stepKey="seeResult"/> +``` + +`myAction` will be executed after the action, which has `stepKey="fillField"`. + +## Examples + +{%raw%} +The following example contains four actions: + +1. [Open the Sign In page for a Customer](#example-step1). +2. [Enter a customer's email](#example-step2). +3. [Enter a customer's password](#example-step3). +4. [Click the Sign In button](#example-step4). + + ```xml + <amOnPage url="{{StorefrontCustomerSignInPage.url}}" stepKey="amOnSignInPage"/> + <fillField userInput="$$customer.email$$" selector="{{StorefrontCustomerSignInFormSection.emailField}}" stepKey="fillEmail"/> + <fillField userInput="$$customer.password$$" selector="{{StorefrontCustomerSignInFormSection.passwordField}}" stepKey="fillPassword"/> + <click selector="{{StorefrontCustomerSignInFormSection.signInAccountButton}}" stepKey="clickSignInAccountButton"/> + ``` + +### 1. Open the Sign In page for a customer {#example-step1} + +```xml +<amOnPage url="{{StorefrontCustomerSignInPage.url}}" stepKey="amOnSignInPage"/> +``` + +The Customer Sign In page is declared in the `.../Customer/Page/StorefrontCustomerSignInPage.xml` file. +The given relative URI is declared in `StorefrontCustomerSignInPage.url`. + +Source code (`StorefrontCustomerSignInPage.xml` ): + +```xml +<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/PageObject.xsd"> + <page name="StorefrontCustomerSignInPage" url="/customer/account/login/" module="Magento_Customer"> + <section name="StorefrontCustomerSignInFormSection" /> + </page> +</config> +``` + +[`<amOnPage>`](#amonpage) is an action that opens a page for a given URI. It has a key `"amOnSignInPage"` that will be used as a reference for merging needs in other modules. +This action uses the `url` attribute value for the given relative URI to open a browser page. +Here, `url` contains a pointer to a `url` attribute of the `StorefrontCustomerSignInPage`. + +### 2. Enter a customer's email {#example-step2} + +```xml +<fillField userInput="$$customer.email$$" selector="{{StorefrontCustomerSignInFormSection.emailField}}" stepKey="fillEmail"/> +``` + +[`<fillField>`](#fillfield) fills a text field with the given string. + +The customer's email is stored in the `email` parameter of the `customer` entity created somewhere earlier in the test using a [`<createData>`](#createdata) tag. +`userInput` points to that data. + +`selector` points to the field where you enter the data. +A required selector is stored in the `emailField` element of the `StorefrontCustomerSignInFormSection` section. + +This section is declared in `.../Customer/Section/StorefrontCustomerSignInFormSection.xml` file: +{: #section-code} + +```xml +<config 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"/> + </section> +</config> +``` + +### 3. Enter a customer's password {#example-step3} + +```xml +<fillField userInput="$$customer.password$$" selector="{{StorefrontCustomerSignInFormSection.passwordField}}" stepKey="fillPassword"/> +``` + +This `<action>` is very similar to the `<action>` in a previous step. +The only difference is that different data is assigned to the attributes, which set a field with a password. + +### 4. Click the Sign In button {#example-step4} + +```xml +<click selector="{{StorefrontCustomerSignInFormSection.signInAccountButton}}" stepKey="clickSignInAccountButton"/> +``` + +Here, [`<click>`](#click) performs a click on a button that can be found by the selector that is stored in the `signInAccountButton` of the `StorefrontCustomerSignInFormSection`. +See the `StorefrontCustomerSignInPage.xml` file code in [step 2](#section-code) +{%endraw%}. + +## Actions returning a variable + +The following test actions return a variable: + +* [grabAttributeFrom](#grabattributefrom) +* [grabCookie](#grabcookie) +* [grabFromCurrentUrl](#grabfromcurrenturl) +* [grabMultiple](#grabmultiple) +* [grabPageSource](#grabpagesource) +* [grabTextFrom](#grabtextfrom) +* [grabValueFrom](#grabvaluefrom) +* [executeJS](#executejs) + +Learn more in [Using data returned by test actions](../data.html#use-data-returned-by-test-actions). + +## Actions handling data entities + +The following test actions handle data entities using [metadata](../metadata.html): + +* [createData](#createdata) +* [deleteData](#deletedata) +* [updateData](#updatedata) +* [getData](#getdata) + +Learn more in [Handling a REST API response](../metadata.html#rest-response). + +## Reference + +The following list contains reference documentation about all action elements available in the MFTF. +If the description of an element does not include a link to Codeception analogue, it means that the action is developed by Magento for specific MFTF needs. + +### acceptPopup + +Accepts the current popup visible on the page. + +See [acceptPopup docs on codeception.com](http://codeception.com/docs/modules/WebDriver#acceptPopup){:target="_blank"}. + +Attribute|Type|Use|Description +---|---|---|--- +`stepKey`|string|required| A unique identifier of the action. +`skipReadiness`|boolean|optional| A flag to skip the readiness check. +`before`|string|optional| `stepKey` of action that must be executed next. +`after`|string|optional| `stepKey` of preceding action. + +#### Example + +```xml +<!-- Accept the current popup visible on the page. --> +<acceptPopup stepKey="acceptPopup"/> +``` + +### amOnPage + +Opens the page by the URL relative to the one set in the `MAGENTO_BASE_URL` configuration variable. + +See [amOnPage docs on codeception.com](http://codeception.com/docs/modules/WebDriver#amOnPage){:target="_blank"}. + +Attribute|Type|Use|Description +---|---|---|--- +`url`|string|optional| A path to the page relative to the `MAGENTO_BASE_URL`. +`stepKey`|string|required|A unique identifier of the action. +`skipReadiness`|boolean|optional| A flag to skip the readiness check. +`before`|string|optional| `stepKey` of action that must be executed next. +`after`|string|optional| `stepKey` of preceding action. + +#### Example + +```xml +<!-- Open the `(baseURL)/admin` page. --> +<amOnPage url="/admin" stepKey="goToLogoutPage"/> +``` + +### amOnSubdomain + +Takes the base URL and changes the subdomain. + +See [amOnSubdomain docs on codeception.com](http://codeception.com/docs/modules/WebDriver#amOnSubdomain){:target="_blank"}. + +Attribute|Type|Use|Description +---|---|---|--- +`url`|string|optional| The name of the subdomain. +`stepKey`|string|required| A unique identifier of the action. +`skipReadiness`|boolean|optional| A flag to skip the readiness check. +`before`|string|optional| `stepKey` of action that must be executed next. +`after`|string|optional| `stepKey` of preceding action. + +#### Example + +Pre-condition: the current base URL is `https://www.magento.com`. + +```xml +<!-- Change the sub-domain to `https://devdocs.magento.com`. --> +<amOnSubdomain url="devdocs" stepKey="changeSubdomain"/> +<!-- Open the page `https://devdocs.magento.com` --> +<amOnPage url="/" stepKey="goToDataPage"/> +``` + +### amOnUrl + +Opens a page by the absolute URL. + +See [amOnUrl docs on codeception.com](http://codeception.com/docs/modules/WebDriver#amOnUrl){:target="_blank"}. + +Attribute|Type|Use|Description +---|---|---|--- +`url`|string|optional| The absolute URL to be used in subsequent steps. +`stepKey`|string|required| A unique identifier of the action. +`skipReadiness`|boolean|optional| A flag to skip the readiness check. +`before`|string|optional| `stepKey` of action that must be executed next. +`after`|string|optional| `stepKey` of preceding action. + +#### Example + +```xml +<!-- Set url to be used in the next steps to https://www.magento.com/ --> +<amOnUrl url="https://www.magento.com/" stepKey="amOnUrl"/> +``` + +### appendField + +See [appendField docs on codeception.com](http://codeception.com/docs/modules/WebDriver#appendField){:target="_blank"}. + +Attribute|Type|Use|Description +---|---|---|--- +`selector`|string|optional| The selector used to identify the form field. +`userInput`|string|optional| Value to append to the form field. +`stepKey`|string|required| A unique identifier of the action. +`skipReadiness`|boolean|optional| A flag to skip the readiness check. +`before`|string|optional| `stepKey` of action that must be executed next. +`after`|string|optional| `stepKey` of preceding action. + +#### Example + +```xml +<!-- Append the "Sample Text" string to the selected input element --> +<appendField userInput="Sample Text" selector="input#name" stepKey="appendSuffix"/> +``` + +### attachFile + +See [attachFile docs on codeception.com](http://codeception.com/docs/modules/WebDriver#attachFile){:target="_blank"}. + +Attribute|Type|Use|Description +---|---|---|--- +`selector`|string|optional|The selector identifying the corresponding HTML element (`<input type="file">`). +`userInput`|string|optional|The name of attaching file. The file must be placed in the `tests/_data` directory. +`stepKey`|string|required| A unique identifier of the action. +`skipReadiness`|boolean|optional| A flag to skip the readiness check. +`before`|string|optional| `stepKey` of action that must be executed next. +`after`|string|optional| `stepKey` of preceding action. + +#### Example + +```xml +<!-- Upload a file from the `tests/_data` directory with the `image.png` name to the selected input element. --> +<attachFile userInput="image.png" selector="input#imgUpload" stepKey="uploadFile"/> +``` + +### cancelPopup + +See [cancelPopup docs on codeception.com](http://codeception.com/docs/modules/WebDriver#cancelPopup){:target="_blank"}. + +Attribute|Type|Use|Description +---|---|---|--- +`stepKey`|string|required| A unique identifier of the action. +`skipReadiness`|boolean|optional| A flag to skip the readiness check. +`before`|string|optional| `stepKey` of action that must be executed next. +`after`|string|optional| `stepKey` of preceding action. + +#### Example + +```xml +<!-- Cancel the current popup visible on the page. --> +<cancelPopup stepKey="cancelPopup"/> +``` + +### checkOption + +See [checkOption docs on codeception.com](http://codeception.com/docs/modules/WebDriver#checkOption){:target="_blank"}. + +Attribute|Type|Use|Description +---|---|---|--- +`selector`|string|optional| The selector identifying the corresponding HTML element. +`stepKey`|string|required| A unique identifier of the action. +`skipReadiness`|boolean|optional| A flag to skip the readiness check. +`before`|string|optional| `stepKey` of action that must be executed next. +`after`|string|optional| `stepKey` of preceding action. + +#### Example + +```xml +<!-- Ensure the checkbox `<input type="checkbox" id="checkbox" ... >...</input>` is checked. --> +<checkOption selector="input#checkbox" stepKey="checkCheckbox"/> +``` + +### clearField + +Clears a text input field. +Equivalent to using [`<fillField>`](#fillfield) with an empty string. + +Attribute|Type|Use|Description +---|---|---|--- +`selector`|string|required| The selector identifying the corresponding HTML element to be cleared. +`stepKey`|string|required| A unique identifier of the action. +`skipReadiness`|boolean|optional| A flag to skip the readiness check. +`before`|string|optional| `stepKey` of action that must be executed next. +`after`|string|optional| `stepKey` of preceding action. + +#### Example + +```xml +<!-- Clear the selected field. --> +<clearField selector="input#name" stepKey="clearField"/> +``` + +### click + +See [click docs on codeception.com](http://codeception.com/docs/modules/WebDriver#click){:target="_blank"}. + +Attribute|Type|Use|Description +---|---|---|--- +`selector`|string|optional| The selector identifying the corresponding HTML element. +`selectorArray`|string|optional| Selects an element as a key value array. See [strict locator](http://codeception.com/docs/modules/WebDriver#locating-elements){:target="_blank"}. +`userInput`|string|optional| Data to be sent with the click. +`stepKey`|string|required| A unique identifier of the action. +`skipReadiness`|boolean|optional| A flag to skip the readiness check. +`before`|string|optional| `stepKey` of action that must be executed next. +`after`|string|optional| `stepKey` of preceding action. + +#### Example + +```xml +<!-- Click the selected button. --> +<click selector="button#clickable" stepKey="clickButton"/> +``` + +```xml +<!-- Click on the "<a href=...>Login</a>" link. --> +<click selectorArray="['link' => 'Login']" stepKey="clickButton2"/> +``` + +### clickWithLeftButton + +See [clickWithLeftButton docs on codeception.com](http://codeception.com/docs/modules/WebDriver#clickWithLeftButton){:target="_blank"}. + +Attribute|Type|Use|Description +---|---|---|--- +`selector`|string|optional| The selector identifying the corresponding HTML element. +`selectorArray`|string|optional| Selects an element as a key value array; See [strict locator]. +`x`|string|optional| The x-axis value in pixels for the click location. +`y`|string|optional| The y-axis value in pixels for the click location. +`stepKey`|string|required|A unique identifier of the action. +`skipReadiness`|boolean|optional| A flag to skip the readiness check. +`before`|string|optional| `stepKey` of action that must be executed next. +`after`|string|optional| `stepKey` of preceding action. + +#### Examples + +```xml +<!-- Left click on the center of the `<button id="clickable" />` element. --> +<clickWithLeftButton selector="button#clickable" stepKey="clickButton1"/> +``` + +```xml +<!-- Left click on the point that is 50 px from the top of the window and 50 px from the left of the window. --> +<clickWithLeftButton x="50" y="50" stepKey="clickButton2"/> +``` + +```xml +<!-- Left click on the point that is 50 px from the top and 50 px from the left of of the `<button id="clickable" />` element.. --> +<clickWithLeftButton selector="button#clickable" x="50" y="50" stepKey="clickButton3"/> +``` + +### clickWithRightButton + +See [clickWithRightButton docs on codeception.com](http://codeception.com/docs/modules/WebDriver#clickWithRightButton){:target="_blank"}. + +Attribute|Type|Use|Description +---|---|---|--- +`selector`|string|optional| The selector identifying the corresponding HTML element. +`selectorArray`|string|optional| Selects an element as a key value array; See [strict locator]. +`x`|string|optional| The x-axis value in pixels for the click location. +`y`|string|optional| The y-axis value in pixels for the click location. +`stepKey`|string|required| A unique identifier of the action. +`skipReadiness`|boolean|optional| A flag to skip the readiness check. +`before`|string|optional| `stepKey` of action that must be executed next. +`after`|string|optional| `stepKey` of preceding action. + +#### Examples + +```xml +<!-- Right click on the center of the `<button id="clickable" />` element. --> +<clickWithRightButton selector="button#clickable" stepKey="clickButton1"/> +``` + +```xml +<!-- Right click on the point that is 50 px from the top of the window and 50 px from the left of the window. --> +<clickWithRightButton x="50" y="50" stepKey="clickButton2"/> +``` + +```xml +<!-- Right click on the point that is 50 px from the top and 50 px from the left of of the `<button id="clickable" />` element.. --> +<clickWithRightButton selector="button#clickable" x="50" y="50" stepKey="clickButton3"/> +``` + +### closeAdminNotification + +Remove from the DOM all elements with the CSS classes `.modal-popup` or `.modals-overlay`. + +Attribute|Type|Use|Description +---|---|---|--- +`stepKey`|string|required| A unique identifier of the action. +`skipReadiness`|boolean|optional| A flag to skip the readiness check. +`before`|string|optional| `stepKey` of action that must be executed next. +`after`|string|optional| `stepKey` of preceding action. + +#### Example + +```xml +<!-- Remove elements of the `.modal-popup` or `.modals-overlay` CSS classes. --> +<closeAdminNotification stepKey="closeAdminNotification"/> +``` + +### closeTab + +See [closeTab docs on codeception.com](http://codeception.com/docs/modules/WebDriver#closeTab){:target="_blank"}. + +Attribute|Type|Use|Description +---|---|---|--- +`stepKey`|string|required| A unique identifier of the action. +`skipReadiness`|boolean|optional| A flag to skip the readiness check. +`before`|string|optional| `stepKey` of action that must be executed next. +`after`|string|optional| `stepKey` of preceding action. + +#### Example + +```xml +<!-- Close the active tab. --> +<closeTab stepKey="closeTab"/> +``` + +### comment + +Allows input of a string as a PHP code comment. +This tag is not executed. +It is intended to aid documentation and clarity of tests. + +Attribute|Type|Use|Description +---|---|---|--- +`userInput`|string|required| PHP comment that will be written in generated test file. +`stepKey`|string|required| A unique identifier of the action. +`skipReadiness`|boolean|optional| A flag to skip the readiness check. +`before`|string|optional| `stepKey` of action that must be executed next. +`after`|string|optional| `stepKey` of preceding action. + +```xml +<!-- Open the specified page and print a comment "I am on the login page" in the log during test execution. --> +<amOnPage url="/login" stepKey="goToLoginPage"/> +<comment userInput="I am on the login page" stepKey="loginPageComment"/> +``` + +### conditionalClick + +Conditionally clicks on an element if, and only if, another element is visible or not. + +Attribute|Type|Use|Description +---|---|---|--- +`selector`|string|optional| The selector identifying the HTML element to be clicked. +`dependentSelector`|string|optional| The selector of the HTML element whose visibility is checked for to activate the click. +`visible`|boolean|optional| Determines whether the conditional click is activated by the element being visible or hidden. +`stepKey`|string|required| A unique identifier of the action. +`skipReadiness`|boolean|optional| A flag to skip the readiness check. +`before`|string|optional| `stepKey` of action that must be executed next. +`after`|string|optional| `stepKey` of preceding action. + +#### Examples + +```xml +<!-- Click on the element with `id="foo"` if the element with `id="bar"` is visible. --> +<conditionalClick selector="#foo" dependentSelector="#bar" visible="true" stepKey="click1"/> +``` + +### createData + +Creates an entity (for example, a category or product). +To create an entity, the MFTF makes a `POST` request to the Magento API according to the [data](../data.html) and [metadata](../metadata.html) of the entity to be created. + +Attribute|Type|Use|Description +---|---|---|--- +`entity`|string|required| Type of entity to be created. +`storeCode`|string|optional| ID of the store within which the data is created. +`stepKey`|string|required| A unique identifier of the action. +`skipReadiness`|boolean|optional| A flag to skip the readiness check. +`before`|string|optional| `stepKey` of action that must be executed next. +`after`|string|optional| `stepKey` of preceding action. + +It can optionally contain one or more `requiredEntity` child elements. + +#### Example + +```xml +<!-- Create an entity with the "SampleProduct" name. --> +<createData entity="SampleProduct" stepKey="createSampleProduct"/> +``` + +#### requiredEntity + +Specify relationships amongst data to be created. +For example, a complex Product object may contain within it a pointer (an ID) to a complex Category object. + +##### Example + +```xml +<!-- Create an entity with the "SampleCategory" name. --> +<createData entity="SampleCategory" stepKey="createCategory"/> +<!-- Create the "SampleProduct" product in that category. --> +<createData entity="SampleProduct" stepKey="createProduct"> + <requiredEntity createDataKey="createCategory"/> +</createData> +``` + +Attribute|Type|Use|Description +---|---|---|--- +`createDataKey`|string|required| Name of the required entity. +`stepKey`|string|required| A unique identifier of the action. +`skipReadiness`|boolean|optional| A flag to skip the readiness check. +`before`|string|optional| `stepKey` of action that must be executed next. +`after`|string|optional| `stepKey` of preceding action. + +#### field + +Persists a custom field (as a part of the entity) overriding the matching declaration in static data. +This field is replaced at a top level only (nested values such as custom attributes or extension attributes are not replaced). + +Attribute|Type|Use|Description +---|---|---|--- +`key`|string|required| Name of the field to be replaced or added. + +##### Example + +To overwrite the `name` field in a particular product, specify a field element during its creation. + +```xml +<createData entity="SampleProduct" stepKey="createProduct"> + <field key="name">myCustomProductName</field> +</createData> +``` + +### deleteData + +Delete an entity that was previously created. + +Attribute|Type|Use|Description +---|---|---|--- +`createDataKey`|string|optional| Reference to `stepKey` of the `createData` action . +`url`|string|optional| REST API route to send a DELETE request. +`storeCode`|string|optional| ID of the store from which to delete the data. +`stepKey`|string|required| A unique identifier of the action. +`skipReadiness`|boolean|optional| A flag to skip the readiness check. +`before`|string|optional| `stepKey` of action that must be executed next. +`after`|string|optional| `stepKey` of preceding action. + +#### Examples + +Delete the entity that was previously created using [`createData`](#createdata) in the scope of the [test](../test.html#test-tag). + +1. Create _SampleCategory_: + +```xml +<createData entity="SampleCategory" stepKey="createCategory"/> +``` + +1. Delete _SampleCategory_: + +```xml +<deleteData createDataKey="createCategory" stepKey="deleteCategory"/> +``` + +#### Example of existing data deletion + +Delete an entity using [REST API]({{ site.gdeurl23 }}rest/bk-rest.html) request to the corresponding route: + +```xml +<grabFromCurrentUrl regex="/^.+id\/([\d]+)/" stepKey="grabId"/> +<deleteData url="V1/categories/{$grabId}" stepKey="deleteCategory"/> +``` + +### dontSee + +See [the codeception.com documentation for more information about this action](http://codeception.com/docs/modules/WebDriver#dontSee){:target="_blank"}. + +Attribute|Type|Use|Description +---|---|---|--- +`userInput`|string|optional| Value for the form field. +`selector`|string|optional| The selector identifying the corresponding HTML element. +`selectorArray`|string|optional| Array of selectors to evaluate. +`stepKey`|string|required| A unique identifier of the action. +`skipReadiness`|boolean|optional| A flag to skip the readiness check. +`before`|string|optional| `stepKey` of action that must be executed next. +`after`|string|optional| `stepKey` of preceding action. + +#### Example + +```xml +<!-- Check that the page does not contain the `<h2 id="title">Sample title</h2>` element. --> +<dontSee userInput="Sample title" selector="h2#title" stepKey="dontSeeTitle"/> +``` + +### dontSeeCheckboxIsChecked + +See [dontSeeCheckboxIsChecked docs on codeception.com](http://codeception.com/docs/modules/WebDriver#dontSeeCheckboxIsChecked){:target="_blank"}. + +Attribute|Type|Use|Description +---|---|---|--- +`selector`|string|optional| The selector identifying the corresponding HTML element. +`stepKey`|string|required| A unique identifier of the action. +`skipReadiness`|boolean|optional| A flag to skip the readiness check. +`before`|string|optional| `stepKey` of action that must be executed next. +`after`|string|optional| `stepKey` of preceding action. + +#### Example + +```xml +<!-- Verify that the page does not contain the `<input type="checkbox" id="option1" ... >...</input>` element. --> +<dontSeeCheckboxIsChecked selector="input#option1" stepKey="checkboxNotChecked"/> +``` + +### dontSeeCookie + +See [dontSeeCookie docs on codeception.com](http://codeception.com/docs/modules/WebDriver#dontSeeCookie){:target="_blank"}. + +Attribute|Type|Use|Description +---|---|---|--- +`userInput`|string|optional| Value for the form field. +`parameterArray`|string|optional| Parameters to search for within the cookie. +`stepKey`|string|required| A unique identifier of the action. +`skipReadiness`|boolean|optional| A flag to skip the readiness check. +`before`|string|optional| `stepKey` of action that must be executed next. +`after`|string|optional| `stepKey` of preceding action. + +#### Examples + +```xml +<!-- Verify that there is no cookie with the given name `cookie1`. --> +<dontSeeCookie userInput="cookie1" stepKey="cookie1NotPresent"/> +``` + +```xml +<!-- Verify that there is no cookie with the given name `cookie1` from the domain `www.example.com`. --> +<dontSeeCookie userInput="cookie1" parameterArray="['domainName' => '.example.com']" stepKey="dontSeeCookieInExampleDomain"/> +``` + +### dontSeeCurrentUrlEquals + +See [dontSeeCurrentUrlEquals docs on codeception.com](http://codeception.com/docs/modules/WebDriver#dontSeeCurrentUrlEquals){:target="_blank"}. + +Attribute|Type|Use|Description +---|---|---|--- +`url`|string|optional| URL to be compared with the current URL. +`stepKey`|string|required| A unique identifier of the action. +`skipReadiness`|boolean|optional| A flag to skip the readiness check. +`before`|string|optional| `stepKey` of action that must be executed next. +`after`|string|optional| `stepKey` of preceding action. + +#### Example + +```xml +<!-- Verify that the relative URL of the current page does not match `/admin`. --> +<dontSeeCurrentUrlEquals url="/admin" stepKey="notOnAdminPage"/> +``` + +### dontSeeCurrentUrlMatches + +See [dontSeeCurrentUrlMatches docs on codeception.com](http://codeception.com/docs/modules/WebDriver#dontSeeCurrentUrlMatches){:target="_blank"} + +Attribute|Type|Use|Description +---|---|---|--- +`regex`|string|optional| Regular expression against the current URI. +`stepKey`|string|required| A unique identifier of the action. +`skipReadiness`|boolean|optional| A flag to skip the readiness check. +`before`|string|optional| `stepKey` of action that must be executed next. +`after`|string|optional| `stepKey` of preceding action. + +#### Example + +```xml +<!-- Verify that the relative URL of the current page does not match the `~$/users/(\d+)~` regular expression. --> +<dontSeeCurrentUrlMatches regex="~$/users/(\d+)~" stepKey="dontSeeCurrentUrlMatches"/> +``` + +### dontSeeElement + +See [dontSeeElement docs on codeception.com](http://codeception.com/docs/modules/WebDriver#dontSeeElement){:target="_blank"}. + +Attribute|Type|Use|Description +---|---|---|--- +`selector`|string|optional| The selector identifying the corresponding HTML element. +`parameterArray`|string|optional| Parameters to search for within the selected element. +`stepKey`|string|required| A unique identifier of the action. +`skipReadiness`|boolean|optional| A flag to skip the readiness check. +`before`|string|optional| `stepKey` of action that must be executed next. +`after`|string|optional| `stepKey` of preceding action. + +#### Example + +```xml +<!-- Verify that `<div id="box" ... >...</div>` is missing or invisible on the current page. --> +<dontSeeElement selector="div#box" stepKey="dontSeeBox"/> +``` + +### dontSeeElementInDOM + +See [dontSeeElementInDOM docs on codeception.com](http://codeception.com/docs/modules/WebDriver#dontSeeElementInDOM){:target="_blank"}. + +Attribute|Type|Use|Description +---|---|---|--- +`selector`|string|optional| The selector identifying the corresponding HTML element. +`parameterArray`|string|optional| Array of parameters to search for within the selector. +`stepKey`|string|required| A unique identifier of the action. +`skipReadiness`|boolean|optional| A flag to skip the readiness check. +`before`|string|optional| `stepKey` of action that must be executed next. +`after`|string|optional| `stepKey` of preceding action. + +#### Example + +```xml +<!-- Verify that `<div id="box" ... >...</div>` is completely missing on the current page. --> +<dontSeeElementInDOM selector="div#box" stepKey="dontSeeBoxInDOM"/> +``` + +### dontSeeInCurrentUrl + +See [dontSeeInCurrentUrl docs on codeception.com](http://codeception.com/docs/modules/WebDriver#dontSeeInCurrentUrl){:target="_blank"}. + +Attribute|Type|Use|Description +---|---|---|--- +`url`|string|optional| String to search for within the current URL. +`stepKey`|string|required| A unique identifier of the action. +`skipReadiness`|boolean|optional| A flag to skip the readiness check. +`before`|string|optional| `stepKey` of action that must be executed next. +`after`|string|optional| `stepKey` of preceding action. + +#### Example + +```xml +<!-- Verify that the url of the current active tab does not contain the string "/users/". --> +<dontSeeInCurrentUrl url="/users/" stepKey="dontSeeInCurrentUrl"/> +``` + +### dontSeeInField + +See [dontSeeInField docs on codeception.com](http://codeception.com/docs/modules/WebDriver#dontSeeInField){:target="_blank"}. + +Attribute|Type|Use|Description +---|---|---|--- +`selector`|string|optional| The selector identifying the corresponding HTML element. +`selectorArray`|string|optional| Array of selectors to be searched. +`userInput`|string|optional| Value for the form field. +`stepKey`|string|required| A unique identifier of the action. +`skipReadiness`|boolean|optional| A flag to skip the readiness check. +`before`|string|optional| `stepKey` of action that must be executed next. +`after`|string|optional| `stepKey` of preceding action. + +#### Example + +```xml +<!-- Verify that `<input id="field" ... >...</input>` does not contain the text "Sample text". --> +<dontSeeInField userInput="Sample text" selector="input#field" stepKey="dontSeeInField1"/> +``` + +### dontSeeInFormFields + +See [dontSeeInFormFields docs on codeception.com](http://codeception.com/docs/modules/WebDriver#dontSeeInFormFields){:target="_blank"}. + +Attribute|Type|Use|Description +---|---|---|--- +`selector`|string|optional| The selector identifying the corresponding HTML element. +`parameterArray`|string|optional| Array of name/value pairs of the form fields to check against. +`stepKey`|string|required| A unique identifier of the action. +`skipReadiness`|boolean|optional| A flag to skip the readiness check. +`before`|string|optional| `stepKey` of action that must be executed next. +`after`|string|optional| `stepKey` of preceding action. + +#### Example + +```xml +<!-- Verify that `<form name="myform" ... >...</form>` with the input elements `<input name="input1">...</input>` and `<input name="input2">...</input>`, do not have the values of `value1` and `value2` respectively. --> +<dontSeeInFormFields selector="form[name=myform]" parameterArray="['input1' => 'value1', 'input2' => 'value2']" stepKey="dontSeeInFormFields"/> +``` + +### dontSeeInPageSource + +See [dontSeeInPageSource docs on codeception.com](http://codeception.com/docs/modules/WebDriver#dontSeeInPageSource){:target="_blank"}. + +Attribute|Type|Use|Description +---|---|---|--- +`userInput`|string|optional| Value for the form field. +`stepKey`|string|required| A unique identifier of the action. +`skipReadiness`|boolean|optional| A flag to skip the readiness check. +`before`|string|optional| `stepKey` of action that must be executed next. +`after`|string|optional| `stepKey` of preceding action. + +#### Example + +```xml +<!-- Verify that the page source does not contain the string "Sample text". --> +<dontSeeInPageSource userInput="Sample text" stepKey="dontSeeInPageSource"/> +``` + +### dontSeeInSource + +See [dontSeeInSource docs on codeception.com](http://codeception.com/docs/modules/WebDriver#dontSeeInSource){:target="_blank"}. + +Attribute|Type|Use|Description +---|---|---|--- +`html`|string|optional| HTML code to search for within the source code. +`stepKey`|string|required| A unique identifier of the action. +`skipReadiness`|boolean|optional| A flag to skip the readiness check. +`before`|string|optional| `stepKey` of action that must be executed next. +`after`|string|optional| `stepKey` of preceding action. + +#### Example + +```xml +<!-- Verify that the page does not contain the raw source code `<h1>Sample text</h1>`. --> +<dontSeeInSource userInput="<h1>Sample text</h1>" stepKey="dontSeeInSource"/> +``` + +### dontSeeInTitle + +See [dontSeeInTitle docs on codeception.com](http://codeception.com/docs/modules/WebDriver#dontSeeInTitle){:target="_blank"}. + +Attribute|Type|Use|Description +---|---|---|--- +`userInput`|string|optional| Value to be located in the page title. +`stepKey`|string|required| A unique identifier of the action. +`skipReadiness`|boolean|optional| A flag to skip the readiness check. +`before`|string|optional| `stepKey` of action that must be executed next. +`after`|string|optional| `stepKey` of preceding action. + +#### Example + +```xml +<!-- Verify that the title of the current active window does not contain the text "Page Title". --> +<dontSeeInTitle userInput="Page Title" stepKey="dontSeeInTitle"/> +``` + +### dontSeeJsError + +Ensure that the current page does not have JavaScript errors. + +Attribute|Type|Use|Description +---|---|---|--- +`stepKey`|string|required| A unique identifier of the action. +`skipReadiness`|boolean|optional| A flag to skip the readiness check. +`before`|string|optional| `stepKey` of action that must be executed next. +`after`|string|optional| `stepKey` of preceding action. + +#### Example + +```xml +<!-- Verify there are no JavaScript errors in the current active window. --> +<dontSeeJsError stepKey="dontSeeJsError"/> +``` + +### dontSeeLink + +See [dontSeeLink docs on codeception.com](http://codeception.com/docs/modules/WebDriver#dontSeeLink){:target="_blank"}. + +Attribute|Type|Use|Description +---|---|---|--- +`userInput`|string|optional| Text of the link field to search for. +`url`|string|optional| Value of the href attribute to search for. +`stepKey`|string|required| A unique identifier of the action. +`skipReadiness`|boolean|optional| A flag to skip the readiness check. +`before`|string|optional| `stepKey` of action that must be executed next. +`after`|string|optional| `stepKey` of preceding action. + +#### Examples + +```xml +<!-- Verify that there is no hyperlink tag on the page with the text "External link". --> +<dontSeeLink userInput="External link" stepKey="dontSeeLink"/> +``` + +```xml +<!-- Verify that there is no hyperlink tag with the text "External link" and the `href` attribute of `/admin`. --> +<dontSeeLink userInput="External link" url="/admin" stepKey="dontSeeAdminLink"/> +``` + +### dontSeeOptionIsSelected + +See [dontSeeOptionIsSelected docs on codeception.com](http://codeception.com/docs/modules/WebDriver#dontSeeOptionIsSelected){:target="_blank"}. + +Attribute|Type|Use|Description +---|---|---|--- +`selector`|string|optional| The selector identifying the corresponding select element. +`userInput`|string|optional| Name of the option to look for. +`stepKey`|string|required| A unique identifier of the action. +`skipReadiness`|boolean|optional| A flag to skip the readiness check. +`before`|string|optional| `stepKey` of action that must be executed next. +`after`|string|optional| `stepKey` of preceding action. + +#### Example + +```xml +<!-- Verify that `<select id="myselect" ... >...</select>` does not have the option `option1` selected --> +<dontSeeOptionIsSelected userInput="option1" selector="select#myselect" stepKey="dontSeeOption1"/> +``` + +### doubleClick + +See [doubleClick docs on codeception.com](http://codeception.com/docs/modules/WebDriver#doubleClick){:target="_blank"}. + +Attribute|Type|Use|Description +---|---|---|--- +`selector`|string|optional| The selector identifying the corresponding HTML element. +`stepKey`|string|required| A unique identifier of the action. +`skipReadiness`|boolean|optional| A flag to skip the readiness check. +`before`|string|optional| `stepKey` of action that must be executed next. +`after`|string|optional| `stepKey` of preceding action. + +#### Example + +```xml +<!-- Click the selected element twice in succession. --> +<doubleClick selector="button#mybutton" stepKey="doubleClickButton"/> +``` + +### dragAndDrop + +See [dragAndDrop docs on codeception.com](http://codeception.com/docs/modules/WebDriver#dragAndDrop){:target="_blank"}. + +Attribute|Type|Use|Description +---|---|---|--- +`selector1`|string|optional|A selector for the HTML element to drag. +`selector2`|string|optional|A selector for the HTML element to drop onto. +`x`|int|optional| X offset applied to drag-and-drop destination. +`y`|int|optional| Y offset applied to drag-and-drop destination. +`stepKey`|string|required| A unique identifier of the action. +`skipReadiness`|boolean|optional| A flag to skip the readiness check. +`before`|string|optional| `stepKey` of action that must be executed next. +`after`|string|optional| `stepKey` of preceding action. + +#### Examples + +```xml +<!-- Click and drag `<div id="block1" ... >...</div>` to the middle of `<div id="block2" ... >...</div>` --> +<dragAndDrop selector1="div#block1" selector2="div#block2" stepKey="dragAndDrop"/> +``` + +```xml +<!-- Click and drag `<div id="block1" ... >...</div>` to the middle of `<div id="block2" ... >...</div>` with a left offset of 50px and top offset of 50px. --> +<dragAndDrop selector1="#block1" selector2="#block2" x="50" y="50" stepKey="dragAndDrop"/> +``` + +### executeInSelenium + +See [executeInSelenium docs on codeception.com](http://codeception.com/docs/modules/WebDriver#executeInSelenium){:target="_blank"}. + +Attribute|Type|Use|Description +---|---|---|--- +`function`|string|optional| Name of Selenium function to run. +`stepKey`|string|required| A unique identifier of the action. +`skipReadiness`|boolean|optional| A flag to skip the readiness check. +`before`|string|optional| `stepKey` of action that must be executed next. +`after`|string|optional| `stepKey` of preceding action. + +#### Example + +```xml +<!-- Execute the Selenium function `function(\Facebook\WebDriver\Remote\RemoteWebDriver $webdriver) {$webdriver->get('http://google.com');}`. --> +<executeInSelenium function="function(\Facebook\WebDriver\Remote\RemoteWebDriver $webdriver) {$webdriver->get('http://google.com');}" stepKey="executeInSelenium"/> +``` + +### executeJS + +See [executeJS docs on codeception.com](http://codeception.com/docs/modules/WebDriver#executeJS){:target="_blank"}. + +Attribute|Type|Use|Description +---|---|---|--- +`function`|string|optional| JavaScript to be executed. +`stepKey`|string|required| A unique identifier of the action. +`skipReadiness`|boolean|optional| A flag to skip the readiness check. +`before`|string|optional| `stepKey` of action that must be executed next. +`after`|string|optional| `stepKey` of preceding action. + +#### Example + +```xml +<!-- Return the time in seconds since Unix Epoch (January 1, 1970) using the JavaScript Date() function. +To access this value, use `{$returnTime}` in later actions. --> +<executeJS function="return Math.floor(new Date() / 1000);" stepKey="returnTime"/> +``` + +To access this value you would use `{$returnTime}` in later actions. + +### fillField + +See [fillField docs on codeception.com](http://codeception.com/docs/modules/WebDriver#fillField){:target="_blank"}. + +Attribute|Type|Use|Description +---|---|---|--- +`selector`|string|optional| The selector identifying the corresponding HTML element. +`selectorArray`|string|optional| Array of name/value pairs with which to populate the form. +`userInput`|string|optional| Value for the form field. +`stepKey`|string|required| A unique identifier of the action. +`skipReadiness`|boolean|optional| A flag to skip the readiness check. +`before`|string|optional| `stepKey` of action that must be executed next. +`after`|string|optional| `stepKey` of preceding action. + +#### Example + +```xml +<!-- Fill in `<input id="myfield" ... >...</input>` with the text "Sample text". --> +<fillField userInput="Sample text" selector="input#myfield" stepKey="fillField"/> +``` + +### formatMoney + +Attribute|Type|Use|Description +---|---|---|--- +`userInput`|string|optional| Value for the money form field. +`locale`|string|optional| The PHP locale value for the store. +`stepKey`|string|required| A unique identifier of the action. +`skipReadiness`|boolean|optional| A flag to skip the readiness check. +`before`|string|optional| `stepKey` of action that must be executed next. +`after`|string|optional| `stepKey` of preceding action. + +### generateDate + +Generates a date for use in `{$stepKey}` format in other test actions. + +Attribute|Type|Use|Description +---|---|---|--- +`date`|string|required| Date input to parse. Uses the same functionality as the PHP `strtotime()` function. +`format`|string|required| Format in which to save the given date. Uses the same formatting as the PHP `date()` function. +`timezone`|string|optional| Timezone to use when generating date, defaults to `America/Los_Angeles`. +`stepKey`|string|required| A unique identifier of the action. +`skipReadiness`|boolean|optional| A flag to skip the readiness check. +`before`|string|optional| `stepKey` of action that must be executed next. +`after`|string|optional| `stepKey` of preceding action. + +#### Example + +```xml +<!-- Generate a date that is 1 minute after the current date using Pacific Standard Time. For example "07/11/2020 7:00 AM". +To access this value, use `{$generateDate}` in later actions. --> +<generateDate date="+1 minute" format="m/d/Y g:i A" stepKey="generateDate"/> +``` + +### getData + +Gets an entity (for example, a category), from the Magento API according to the data and metadata of the entity type that is requested. + +Attribute|Type|Use|Description +---|---|---|--- +`storeCode`|string|optional| Identifier of the store from which to get the data. +`index`|integer|optional| The index in the returned data array. +`entity`|string|required| Name of the entity from which to get the data. +`stepKey`|string|required| A unique identifier of the action. +`skipReadiness`|boolean|optional| A flag to skip the readiness check. +`before`|string|optional| `stepKey` of action that must be executed next. +`after`|string|optional| `stepKey` of preceding action. + +#### Example + +```xml +<!-- Get the product attribute that was created using `<createData stepKey="productAttributeHandle" ... />`. --> +<getData entity="ProductAttributeOptionGetter" index="1" stepKey="getAttributeOption1Handle"> + <requiredEntity createDataKey="productAttributeHandle"/> +</getData> +``` + +The `ProductAttributeOptionGetter` entity must be defined in the corresponding [data `*.xml`](../data.html). + +This action can optionally contain one or more [requiredEntity](#requiredentity) child elements. + +### grabAttributeFrom + +See [grabAttributeFrom docs on codeception.com](http://codeception.com/docs/modules/WebDriver#grabAttributeFrom){:target="_blank"}. + +Attribute|Type|Use|Description +---|---|---|--- +`selector`|string|optional| The selector identifying the corresponding HTML element. +`userInput`|string|optional| Name of tag attribute to grab. +`stepKey`|string|required| A unique identifier of the action. +`skipReadiness`|boolean|optional| A flag to skip the readiness check. +`before`|string|optional| `stepKey` of action that must be executed next. +`after`|string|optional| `stepKey` of preceding action. + +#### Example + +```xml +<!-- Grab the `title` attribute from `<input id="myinput" ... >...</input>`. +To access this value, use `{$grabAttributeFromInput}` in later actions. --> +<grabAttributeFrom userInput="title" selector="input#myinput" stepKey="grabAttributeFromInput"/> +``` + +### grabCookie + +See [grabCookie docs on codeception.com](http://codeception.com/docs/modules/WebDriver#grabCookie){:target="_blank"}. + +Attribute|Type|Use|Description +---|---|---|--- +`userInput`|string|optional| Name of the cookie to grab. +`parameterArray`|string|optional| Array of cookie parameters to grab. +`stepKey`|string|required| A unique identifier of the action. +`skipReadiness`|boolean|optional| A flag to skip the readiness check. +`before`|string|optional| `stepKey` of action that must be executed next. +`after`|string|optional| `stepKey` of preceding action. + +#### Examples + +```xml +<!-- Grab the cookie with the given name `cookie1`. +To access this value, use `{$grabCookie1}` in later actions. --> +<grabCookie userInput="cookie1" stepKey="grabCookie1"/> +``` + +```xml +<!-- Grab the cookie with the given name `cookie1` from the domain `www.example.com`. +To access this value, use `{$grabCookieExampleDomain}` in later actions. --> +<grabCookie userInput="cookie1" parameterArray="['domainName' => '.example.com']" stepKey="grabCookieExampleDomain"/> +``` + +### grabFromCurrentUrl + +See [grabFromCurrentUrl docs on codeception.com](http://codeception.com/docs/modules/WebDriver#grabFromCurrentUrl){:target="_blank"}.. + +Attribute|Type|Use|Description +---|---|---|--- +`regex`|string|optional| Regular expression against the current URI. +`stepKey`|string|required| A unique identifier of the action. +`skipReadiness`|boolean|optional| A flag to skip the readiness check. +`before`|string|optional| `stepKey` of action that must be executed next. +`after`|string|optional| `stepKey` of preceding action. + +#### Example + +```xml +<!-- Grab the text from the current URL that matches the regex expression `~$/user/(\d+)/~`. +To access this value, use `{$grabFromCurrentUrl}` in later actions. --> +<grabFromCurrentUrl regex="~$/user/(\d+)/~" stepKey="grabFromCurrentUrl"/> +``` + +### grabMultiple + +See [grabMultiple docs on codeception.com](http://codeception.com/docs/modules/WebDriver#grabMultiple){:target="_blank"}.. + +Attribute|Type|Use|Description +---|---|---|--- +`selector`|string|optional| The selector identifying the corresponding HTML element. +`userInput`|string|optional| Name of the tag attribute to grab. +`stepKey`|string|required| A unique identifier of the action. +`skipReadiness`|boolean|optional| A flag to skip the readiness check. +`before`|string|optional| `stepKey` of action that must be executed next. +`after`|string|optional| `stepKey` of preceding action. + +#### Examples + +```xml +<!-- Grab every element on the page with the class `myElement` and return them as an array. +To access this value, use `{$grabAllMyElements}` in later actions. --> +<grabMultiple selector="div.myElement" stepKey="grabAllMyElements"/> +``` + +```xml +<!-- Grab the `href` tag from every `a` element on the page and return them as an array. +To access this value, use `{$grabAllLinks}` in later actions. --> +<grabMultiple userInput="href" selector="a" stepKey="grabAllLinks"/> +``` + +### grabPageSource + +See [grabPageSource docs on codeception.com](http://codeception.com/docs/modules/WebDriver#grabPageSource){:target="_blank"}. + +Attribute|Type|Use|Description +---|---|---|--- +`stepKey`|string|required| A unique identifier of the action. +`skipReadiness`|boolean|optional| A flag to skip the readiness check. +`before`|string|optional| `stepKey` of action that must be executed next. +`after`|string|optional| `stepKey` of preceding action. + +#### Example + +```xml +<!-- Store the page source code as text +To access this value, use `{$grabPageSource}` in later actions. --> +<grabPageSource stepKey="grabPageSource"/> +``` + +### grabTextFrom + +See [grabTextFrom docs on codeception.com](http://codeception.com/docs/modules/WebDriver#grabTextFrom){:target="_blank"}. + +Attribute|Type|Use|Description +---|---|---|--- +`selector`|string|optional| The selector identifying the corresponding HTML element. +`stepKey`|string|required| A unique identifier of the action. +`skipReadiness`|boolean|optional| A flag to skip the readiness check. +`before`|string|optional| `stepKey` of action that must be executed next. +`after`|string|optional| `stepKey` of preceding action. + +#### Example + +```xml +<!-- Store the text currently displayed by the selected element. +To access this value, use `{$grabTitle}` in later actions. --> +<grabTextFrom selector="h2#title" stepKey="grabTitle"/> +``` + +### grabValueFrom + +See [grabValueFrom docs on codeception.com](https://codeception.com/docs/modules/WebDriver#grabValueFrom){:target="_blank"}. + +Attribute|Type|Use|Description +---|---|---|--- +`selector`|string|optional| The selector identifying the corresponding HTML element. +`selectorArray`|string|optional| Array of selectors for the form fields to be selected. +`stepKey`|string|required| A unique identifier of the action. +`skipReadiness`|boolean|optional| A flag to skip the readiness check. +`before`|string|optional| `stepKey` of action that must be executed next. +`after`|string|optional| `stepKey` of preceding action. + +#### Example + +```xml +<!-- Store the value currently entered in <input id="name" ... >...</input>. +To access this value, use `{$grabInputName}` in later actions. --> +<grabValueFrom selector="input#name" stepKey="grabInputName"/> +``` + +### loadSessionSnapshot + +See [loadSessionSnapshot docs on codeception.com](http://codeception.com/docs/modules/WebDriver#loadSessionSnapshot){:target="_blank"}. + +Attribute|Type|Use|Description +---|---|---|--- +`userInput`|string|optional| Name of saved cookie. +`stepKey`|string|required| A unique identifier of the action. +`skipReadiness`|boolean|optional| A flag to skip the readiness check. +`before`|string|optional| `stepKey` of action that must be executed next. +`after`|string|optional| `stepKey` of preceding action. + +#### Example + +```xml +<!-- Load all cookies saved via `<saveSessionSnapshot name="savedSnapshot" ... />`. +To access this value, use the `loadSessionSnapshot` action --> +<loadSessionSnapshot userInput="savedSnapshot" stepKey="loadSnapshot"/> +``` + +### magentoCLI + +Specifies a CLI command to execute in a Magento environment. + +Attribute|Type|Use|Description +---|---|---|--- +`command`|string |optional| CLI command to be executed in Magento environment. +`arguments`|string |optional| Unescaped arguments to be passed in with the CLI command. +`stepKey`|string|required| A unique identifier of the action. +`skipReadiness`|boolean|optional| A flag to skip the readiness check. +`before`|string|optional| `stepKey` of action that must be executed next. +`after`|string|optional| `stepKey` of preceding action. + +#### Example + +```xml +<!-- Re-index all indices via the command line. --> +<magentoCLI command="indexer:reindex" stepKey="reindex"/> +``` + +### makeScreenshot + +See [makeScreenshot docs on codeception.com](http://codeception.com/docs/modules/WebDriver#makeScreenshot){:target="_blank"}. + +Attribute|Type|Use|Description +---|---|---|--- +`userInput`|string|optional| Name of PNG file to be created. +`stepKey`|string|required| A unique identifier of the action. +`skipReadiness`|boolean|optional| A flag to skip the readiness check. +`before`|string|optional| `stepKey` of action that must be executed next. +`after`|string|optional| `stepKey` of preceding action. + +{% include note.html + type="info" + content="The makeScreenshot action does not automatically add the screenshot to Allure reports." + %} + +#### Example + +```xml +<!-- Take a screenshot of the page and save it to the directory `tests/_output/debug` under the name `example.png`. --> +<makeScreenshot userInput="example" stepKey="screenshotPage"/> +``` + +{:.bs-callout .bs-callout-info} +This action does not add a screenshot to the Allure [report](../reporting.html). + +### maximizeWindow + +See [maximizeWindow docs on codeception.com](http://codeception.com/docs/modules/WebDriver#maximizeWindow){:target="_blank"}. + +Attribute|Type|Use|Description +---|---|---|--- +`stepKey`|string|required| A unique identifier of the action. +`skipReadiness`|boolean|optional| A flag to skip the readiness check. +`before`|string|optional| `stepKey` of action that must be executed next. +`after`|string|optional| `stepKey` of preceding action. + +#### Example + +```xml +<!-- Maximize the current window. --> +<maximizeWindow stepKey="maximizeWindow"/> +``` + +### moveBack + +See [moveBack docs on codeception.com](http://codeception.com/docs/modules/WebDriver#moveBack){:target="_blank"}. + +Attribute|Type|Use|Description +---|---|---|--- +`stepKey`|string|required| A unique identifier of the action. +`skipReadiness`|boolean|optional| A flag to skip the readiness check. +`before`|string|optional| `stepKey` of action that must be executed next. +`after`|string|optional| `stepKey` of preceding action. + +#### Example + +```xml +<!-- Move back one page in history. --> +<moveBack stepKey="moveBack"/> +``` + +### moveForward + +See [moveForward docs on codeception.com](http://codeception.com/docs/modules/WebDriver#moveForward){:target="_blank"}.. + +Attribute|Type|Use|Description +---|---|---|--- +`stepKey`|string|required| A unique identifier of the action. +`skipReadiness`|boolean|optional| A flag to skip the readiness check. +`before`|string|optional| `stepKey` of action that must be executed next. +`after`|string|optional| `stepKey` of preceding action. + +```xml +<!-- Move forward one page in history. --> +<moveForward stepKey="moveForward"/> +``` + +### moveMouseOver + +See [moveMouseOver docs on codeception.com](http://codeception.com/docs/modules/WebDriver#moveMouseOver){:target="_blank"}. + +Attribute|Type|Use|Description +---|---|---|--- +`selector`|string|optional| The selector identifying the corresponding HTML element. +`selectorArray`|string|optional| Array of selectors. +`x`|string|optional| Number of pixels on the x-axis to offset from the selected element. +`y`|string|optional| Number of pixels on the y-axis to offset from the selected element. +`stepKey`|string|required| A unique identifier of the action. +`skipReadiness`|boolean|optional| A flag to skip the readiness check. +`before`|string|optional| `stepKey` of action that must be executed next. +`after`|string|optional| `stepKey` of preceding action. + +#### Example + +```xml +<!-- Move the mouse cursor over the selected element. --> +<moveMouseOver selector="button#product1" stepKey="hoverOverProduct1"/> +``` + +```xml +<!-- Move the mouse cursor over the selected element with an offset of 50px from the top and 50px from the left. --> +<moveMouseOver selector="button#product1" x="50" y="50" stepKey="hoverOverProduct2"/> +``` + +### mSetLocale + +Attribute|Type|Use|Description +---|---|---|--- +`userInput`|string|optional| Value of the expected locale. +`locale`|string|optional| Number of the locale value to be set. +`stepKey`|string|required| A unique identifier of the action. +`skipReadiness`|boolean|optional| A flag to skip the readiness check. +`before`|string|optional| `stepKey` of action that must be executed next. +`after`|string|optional| `stepKey` of preceding action. + +### mResetLocale + +Attribute|Type|Use|Description +---|---|---|--- +`stepKey`|string|required| A unique identifier of the action. +`skipReadiness`|boolean|optional| A flag to skip the readiness check. +`before`|string|optional| `stepKey` of action that must be executed next. +`after`|string|optional| `stepKey` of preceding action. + +### openNewTab + +See [openNewTab docs on codeception.com](http://codeception.com/docs/modules/WebDriver#openNewTab){:target="_blank"}. + +Attribute|Type|Use|Description +---|---|---|--- +`stepKey`|string|required| A unique identifier of the action. +`skipReadiness`|boolean|optional| A flag to skip the readiness check. +`before`|string|optional| `stepKey` of action that must be executed next. +`after`|string|optional| `stepKey` of preceding action. + +#### Example + +```xml +<!-- Open and switch to a new browser tab. --> +<openNewTab stepKey="openNewTab"/> +``` + +### parseFloat + +Parses float number with thousands separator. + +Attribute|Type|Use|Description +---|---|---|--- +`userInput`|string|optional| Float value to be parsed. +`stepKey`|string|required| A unique identifier of the action. +`skipReadiness`|boolean|optional| A flag to skip the readiness check. +`before`|string|optional| `stepKey` of action that must be executed next. +`after`|string|optional| `stepKey` of preceding action. + +### pauseExecution + +See [pauseExecution docs on codeception.com](http://codeception.com/docs/modules/WebDriver#pauseExecution){:target="_blank"}. + +Attribute|Type|Use|Description +---|---|---|--- +`stepKey`|string|required| A unique identifier of the action. +`skipReadiness`|boolean|optional| A flag to skip the readiness check. +`before`|string|optional| `stepKey` of action that must be executed next. +`after`|string|optional| `stepKey` of preceding action. + +#### Example + +```xml +<!-- Halt test execution until the `enter` key is pressed to continue. --> +<pauseExecution stepKey="pause"/> +``` + +### performOn + +See [performOn docs on codeception.com](http://codeception.com/docs/modules/WebDriver#performOn){:target="_blank"}. + +Attribute|Type|Use|Description +---|---|---|--- +`selector`|string|optional| The selector identifying the corresponding HTML element. +`function`|string|optional| Function or actions to be taken on the selected element. +`stepKey`|string|required| A unique identifier of the action. +`skipReadiness`|boolean|optional| A flag to skip the readiness check. +`before`|string|optional| `stepKey` of action that must be executed next. +`after`|string|optional| `stepKey` of preceding action. + +### pressKey + +See [pressKey docs on codeception.com](http://codeception.com/docs/modules/WebDriver#pressKey){:target="_blank"}. + +Attribute|Type|Use|Description +---|---|---|--- +`selector`|string|optional| The selector identifying the corresponding HTML element. +`userInput`|string|optional| Key to be pressed. +`parameterArray`|string|optional| Array of keys to be pressed and functions to be run for the action. +`stepKey`|string|required| A unique identifier of the action. +`skipReadiness`|boolean|optional| A flag to skip the readiness check. +`before`|string|optional| `stepKey` of action that must be executed next. +`after`|string|optional| `stepKey` of preceding action. + +#### Examples + +```xml +<!-- Press the `a` key within the selected area. --> +<pressKey userInput="a" selector="#targetElement" stepKey="pressA"/> +``` + +The `parameterArray` attribute value must begin with `[` and end with `]`. +To press more than one key at a time, wrap the keys in secondary `[]`. + +```xml +<!-- Press the delete within the selected area uses key constants from the WebDriverKeys class. --> +<pressKey selector="#targetElement" parameterArray="[['ctrl', 'a'], \Facebook\WebDriver\WebDriverKeys::DELETE]" stepKey="pressDelete"/> +``` + +### reloadPage + +See [reloadPage docs on codeception.com](http://codeception.com/docs/modules/WebDriver#reloadPage){:target="_blank"}. + +Attribute|Type|Use|Description +---|---|---|--- +`stepKey`|string|required| A unique identifier of the action. +`skipReadiness`|boolean|optional| A flag to skip the readiness check. +`before`|string|optional| `stepKey` of action that must be executed next. +`after`|string|optional| `stepKey` of preceding action. + +#### Example + +```xml +<!-- Reload the current page. --> +<reloadPage stepKey="reloadPage"/> +``` + +### remove + +Removes action by its `stepKey`. + +Attribute|Type|Use|Description +---|---|---|--- +`keyForRemoval`|string|required| Set `stepKey` of the action you want to remove. + +#### Example + +```xml +<!-- Remove an action in the test with the stepKey of `stepKeyToRemove`. --> +<remove keyForRemoval="stepKeyToRemove"/> +``` + +### resetCookie + +See [resetCookie docs on codeception.com](http://codeception.com/docs/modules/WebDriver#resetCookie){:target="_blank"}. + +Attribute|Type|Use|Description +---|---|---|--- +`userInput`|string|optional| Name of the cookie to be reset. +`parameterArray`|string|optional| Array of key/values to get reset within the cookie. +`stepKey`|string|required| A unique identifier of the action. +`skipReadiness`|boolean|optional| A flag to skip the readiness check. +`before`|string|optional| `stepKey` of action that must be executed next. +`after`|string|optional| `stepKey` of preceding action. + +#### Examples + +```xml +<!-- Reset a cookie with the name `cookie1`. --> +<resetCookie userInput="cookie1" stepKey="resetCookie1"/> +``` + +```xml +<!-- Reset a cookie with the given name `cookie1` from the domain `www.example.com`. --> +<resetCookie userInput="cookie1" parameterArray="['domainName' => '.example.com']" stepKey="resetCookieExampleDomain"/> +``` + +### resizeWindow + +See [resizeWindow docs on codeception.com](http://codeception.com/docs/modules/WebDriver#resizeWindow){:target="_blank"}. + +Attribute|Type|Use|Description +---|---|---|--- +`width`|string|optional| The new width of the window in pixels. +`height`|string|optional| The new height of the window in pixels. +`stepKey`|string|required| A unique identifier of the action. +`skipReadiness`|boolean|optional| A flag to skip the readiness check. +`before`|string|optional| `stepKey` of action that must be executed next. +`after`|string|optional| `stepKey` of preceding action. + +#### Examples + +```xml +<!-- Resize the current window to a width of 800px and a height of 600px. --> +<resizeWindow width="800" height="600" stepKey="resizeWindow"/> +``` + +### saveSessionSnapshot + +See [saveSessionSnapshot docs on codeception.com](http://codeception.com/docs/modules/WebDriver#saveSessionSnapshot){:target="_blank"}. + +Attribute|Type|Use|Description +---|---|---|--- +`userInput`|string|optional| Name of snapshot where cookies are to be saved. +`stepKey`|string|required| A unique identifier of the action. +`skipReadiness`|boolean|optional| A flag to skip the readiness check. +`before`|string|optional| `stepKey` of action that must be executed next. +`after`|string|optional| `stepKey` of preceding action. + +#### Example + +```xml +<!-- Save all of the current cookies under the name `savedSnapshot`. --> +<saveSessionSnapshot userInput="savedSnapshot" stepKey="saveCurrentCookies"/> +``` + +### scrollTo + +See [scrollTo docs on codeception.com](http://codeception.com/docs/modules/WebDriver#scrollTo){:target="_blank"}. + +Attribute|Type|Use|Description +---|---|---|--- +`selector`|string|optional| The selector identifying the corresponding HTML element. +`selectorArray`|string|optional| Array of selectors to return. +`x`|string|optional| x offset of the element to be scrolled to. +`y`|string|optional| y offset of the element to be scrolled to. +`stepKey`|string|required| A unique identifier of the action. +`skipReadiness`|boolean|optional| A flag to skip the readiness check. +`before`|string|optional| `stepKey` of action that must be executed next. +`after`|string|optional| `stepKey` of preceding action. + +#### Examples + +```xml +<!-- Move the page to the middle of the selected area. --> +<scrollTo selector="div#anchor" stepKey="scrollToAnchor"/> +``` + +```xml +<!-- Move the page to the middle of the selected area with an offset of 50px from the top and 50px from the left. --> +<scrollTo selector="div#anchor" x="50" y="50" stepKey="scrollToAnchor2"/> +``` + +### scrollToTopOfPage + +A convenience function that executes `window.scrollTo(0,0)` as JavaScript, thus returning to the top of the page. + +Attribute|Type|Use|Description +---|---|---|--- +`stepKey`|string|required| A unique identifier of the action. +`skipReadiness`|boolean|optional| A flag to skip the readiness check. +`before`|string|optional| `stepKey` of action that must be executed next. +`after`|string|optional| `stepKey` of preceding action. + +#### Examples + +```xml +<!-- Move the page to the uppermost, leftmost position. --> +<scrollToTopOfPage stepKey="scrollToTopOfPages"/> +``` + +### searchAndMultiSelectOption + +Search for and select options from a Magento multi-select drop-down menu. +For example, the drop-down menu you use to assign Products to Categories. + +Attribute|Type|Use|Description +---|---|---|--- +`selector`|string|required|The selector of a multi select HTML element (drop-down menu). +`parameterArray`|array|required| Items to search and select in the selected drop-down menu. +`requiredAction`|boolean|optional|Clicks **Done** after selections if `true`. +`stepKey`|string|required| A unique identifier of the action. +`skipReadiness`|boolean|optional| A flag to skip the readiness check. +`before`|string|optional| `stepKey` of action that must be executed next. +`after`|string|optional| `stepKey` of preceding action. + +#### Example + +```xml +<!-- Search and select for "Item 1" amd "Item 2" in the Magento multiselect element with the id of `multiSelect`. --> +<searchAndMultiSelectOption selector="#multiSelect" parameterArray="['Item 1', 'Item 2']" stepKey="searchAndMultiSelect1"/> +``` + +On this test step the MFTF: + +1. Searches for a drop-down HTML element that matches the `#stuff` selector. +2. Opens the drop-down menu. +3. Enters **Item 1** in a search field of the drop-down element. +4. Selects first element from the filtered results. +5. Enters **Item 2** in a search field of the drop-down element. +6. Selects first element from the filtered results. + +### see + +See [see docs on codeception.com](http://codeception.com/docs/modules/WebDriver#see){:target="_blank"}. + +Attribute|Type|Use|Description +---|---|---|--- +`userInput`|string|optional| The text to be searched for within the selector. +`selector`|string|optional| The selector identifying the corresponding HTML element to be searched for. +`selectorArray`|string|optional| Array of selectors to be searched for. +`stepKey`|string|required| A unique identifier of the action. +`skipReadiness`|boolean|optional| A flag to skip the readiness check. +`before`|string|optional| `stepKey` of action that must be executed next. +`after`|string|optional| `stepKey` of preceding action. + +#### Example + +```xml +<!-- Verify that the selected element contains the text "Sample title". --> +<see userInput="Sample title" selector="h2#title" stepKey="seeTitle"/> +``` + +### seeCheckboxIsChecked + +See [seeCheckboxIsChecked docs on codeception.com](http://codeception.com/docs/modules/WebDriver#seeCheckboxIsChecked){:target="_blank"}. + +Attribute|Type|Use|Description +---|---|---|--- +`selector`|string|optional| The selector identifying the corresponding HTML element. +`stepKey`|string|required| A unique identifier of the action. +`skipReadiness`|boolean|optional| A flag to skip the readiness check. +`before`|string|optional| `stepKey` of action that must be executed next. +`after`|string|optional| `stepKey` of preceding action. + +#### Example + +```xml +<!-- Verify `<input type="checkbox" id="option1" ... >...</input>` is checked. --> +<seeCheckboxIsChecked selector="input#option1" stepKey="seeCheckboxChecked"/> +``` + +### seeCookie + +See [seeCookie docs on codeception.com](http://codeception.com/docs/modules/WebDriver#seeCookie){:target="_blank"}. + +Attribute|Type|Use|Description +---|---|---|--- +`userInput`|string|optional| Name of the cookie to be searched for. +`parameterArray`|string|optional| Cookie parameters to be searched for. +`stepKey`|string|required| A unique identifier of the action. +`skipReadiness`|boolean|optional| A flag to skip the readiness check. +`before`|string|optional| `stepKey` of action that must be executed next. +`after`|string|optional| `stepKey` of preceding action. + +#### Examples + +```xml +<!-- Verify that there is a cookie with the given name `cookie1`. --> +<seeCookie userInput="cookie1" stepKey="cookie1Present"/> +``` + +```xml +<!-- Verify that there is a cookie with the given name `cookie1` from the domain `www.example.com`. --> +<seeCookie userInput="cookie1" parameterArray="['domainName' => 'www.example.com']" stepKey="seeCookieInExampleDomain"/> +``` + +### seeCurrentUrlEquals + +See [seeCurrentUrlEquals docs on codeception.com](http://codeception.com/docs/modules/WebDriver#seeCurrentUrlEquals){:target="_blank"}. + +Attribute|Type|Use|Description +---|---|---|--- +`url`|string|optional| The full URL to be searched for. +`stepKey`|string|required| A unique identifier of the action. +`skipReadiness`|boolean|optional| A flag to skip the readiness check. +`before`|string|optional| `stepKey` of action that must be executed next. +`after`|string|optional| `stepKey` of preceding action. + +#### Example + +```xml +<!-- Verify that the relative URL of the current page matches `/admin`. --> +<seeCurrentUrlEquals url="/admin" stepKey="onAdminPage"/> +``` + +### seeCurrentUrlMatches + +See [seeCurrentUrlMatches docs on codeception.com](http://codeception.com/docs/modules/WebDriver#seeCurrentUrlMatches){:target="_blank"}. + +Attribute|Type|Use|Description +---|---|---|--- +`regex`|string|optional| Regular expression against the current URI. +`stepKey`|string|required| A unique identifier of the action. +`skipReadiness`|boolean|optional| A flag to skip the readiness check. +`before`|string|optional| `stepKey` of action that must be executed next. +`after`|string|optional| `stepKey` of preceding action. + +#### Example + +```xml +<!-- Verify that the relative URL of the current page matches the `~$/users/(\d+)~` regular expression. --> +<seeCurrentUrlMatches regex="~$/users/(\d+)~" stepKey="seeCurrentUrlMatches"/> +``` + +### seeElement + +See [seeElement docs on codeception.com](http://codeception.com/docs/modules/WebDriver#seeElement){:target="_blank"}. + +Attribute|Type|Use|Description +---|---|---|--- +`selector`|string|optional| The selector identifying the corresponding HTML element. +`selectorArray`|string|optional| Array of selectors to be searched for. +`parameterArray`|string|optional| Array of parameters to be searched for within the selector. +`stepKey`|string|required| A unique identifier of the action. +`skipReadiness`|boolean|optional| A flag to skip the readiness check. +`before`|string|optional| `stepKey` of action that must be executed next. +`after`|string|optional| `stepKey` of preceding action. + +#### Example + +```xml +<!-- Verify that `<div id="box" ... >...</div>` is available and visible on the current page. --> +<seeElement selector="div#box" stepKey="seeBox"/> +``` + +### seeElementInDOM + +See [seeElementInDOM docs on codeception.com](http://codeception.com/docs/modules/WebDriver#seeElementInDOM){:target="_blank"}. + +Attribute|Type|Use|Description +---|---|---|--- +`selector`|string|optional| The selector identifying the corresponding HTML element. +`parameterArray`|string|optional| Array of parameters to be searched for within the selected element. +`stepKey`|string|required| A unique identifier of the action. +`skipReadiness`|boolean|optional| A flag to skip the readiness check. +`before`|string|optional| `stepKey` of action that must be executed next. +`after`|string|optional| `stepKey` of preceding action. + +#### Example + +```xml +<!-- Verify that `<div id="box" ... >...</div>` is available on the current page. --> +<seeElementInDOM selector="div#box" stepKey="seeBoxInDOM"/> +``` + +### seeInCurrentUrl + +See [seeInCurrentUrl docs on codeception.com](http://codeception.com/docs/modules/WebDriver#seeInCurrentUrl){:target="_blank"}. + +Attribute|Type|Use|Description +---|---|---|--- +`url`|string|optional| String to be searched for within the current URL. +`stepKey`|string|required| A unique identifier of the action. +`skipReadiness`|boolean|optional| A flag to skip the readiness check. +`before`|string|optional| `stepKey` of action that must be executed next. +`after`|string|optional| `stepKey` of preceding action. + +#### Example + +```xml +<!-- Verify that the url of the current active tab contains the string "/users/". --> +<seeInCurrentUrl url="/users/" stepKey="seeInCurrentUrl"/> +``` + +### seeInField + +See [seeInField docs on codeception.com](http://codeception.com/docs/modules/WebDriver#seeInField){:target="_blank"}. + +Attribute|Type|Use|Description +---|---|---|--- +`selector`|string|optional| The selector identifying the corresponding HTML element. +`selectorArray`|string|optional| Array of selectors to be searched. +`userInput`|string|optional| Value to be searched for within the selected form field. +`stepKey`|string|required| A unique identifier of the action. +`skipReadiness`|boolean|optional| A flag to skip the readiness check. +`before`|string|optional| `stepKey` of action that must be executed next. +`after`|string|optional| `stepKey` of preceding action. + +#### Example + +```xml +<!-- Verify that `<input id="field" ... >...</input>` contains the text "Sample text". --> +<seeInField userInput="Sample text" selector="input#field" stepKey="seeInField"/> +``` + +### seeInFormFields + +See [seeInFormFields docs on codeception.com](http://codeception.com/docs/modules/WebDriver#seeInFormFields){:target="_blank"}. + +Attribute|Type|Use|Description +---|---|---|--- +`selector`|string|optional| The selector identifying the corresponding HTML element. +`parameterArray`|string|optional| Array of parameters to be searched for within the selector. +`stepKey`|string|required| A unique identifier of the action. +`skipReadiness`|boolean|optional| A flag to skip the readiness check. +`before`|string|optional| `stepKey` of action that must be executed next. +`after`|string|optional| `stepKey` of preceding action. + +#### Example + +```xml +<!-- Verify that `<form name="myform" ... >...</form>` with the input elements `<input name="input1">...</input>` and `<input name="input2">...</input>`, has the values of `value1` and `value2` respectively. --> +<seeInFormFields selector="form[name=myform]" parameterArray="['input1' => 'value1', 'input2' => 'value2']" stepKey="seeInFormFields"/> +``` + +### seeInPageSource + +See [seeInPageSource docs on codeception.com](http://codeception.com/docs/modules/WebDriver#seeInPageSource){:target="_blank"}. + +Attribute|Type|Use|Description +---|---|---|--- +`html`|string|optional| HTML code to be searched for within the document. +`stepKey`|string|required| A unique identifier of the action. +`skipReadiness`|boolean|optional| A flag to skip the readiness check. +`before`|string|optional| `stepKey` of action that must be executed next. +`after`|string|optional| `stepKey` of preceding action. + +#### Example + +```xml +<!-- Verify that the page source contains the string "Sample text". --> +<seeInPageSource userInput="Sample text" stepKey="seeInPageSource"/> +``` + +### seeInPopup + +See [seeInPopup docs on codeception.com](http://codeception.com/docs/modules/WebDriver#seeInPopup){:target="_blank"}. + +Attribute|Type|Use|Description +---|---|---|--- +`userInput`|string|optional| String to be searched for within the popup. +`stepKey`|string|required| A unique identifier of the action. +`skipReadiness`|boolean|optional| A flag to skip the readiness check. +`before`|string|optional| `stepKey` of action that must be executed next. +`after`|string|optional| `stepKey` of preceding action. + +#### Example + +```xml +<!-- Verify the current popup on the page contains the string "Sample text". --> +<seeInPopup userInput="Sample text" stepKey="seeInPopup"/> +``` + +### seeInSource + +See [seeInSource docs on codeception.com](http://codeception.com/docs/modules/WebDriver#seeInSource){:target="_blank"}. + +Attribute|Type|Use|Description +---|---|---|--- +`html`|string|optional| HTML code to be searched for within the page source. +`stepKey`|string|required| A unique identifier of the action. +`skipReadiness`|boolean|optional| A flag to skip the readiness check. +`before`|string|optional| `stepKey` of action that must be executed next. +`after`|string|optional| `stepKey` of preceding action. + +#### Example + +```xml +<!-- Verify that the page does contains the raw source code `<h1>Sample text</h1>`. --> +<seeInSource userInput="<h1>Sample text</h1>" stepKey="seeInSource"/> +``` + +### seeInTitle + +See [seeInTitle docs on codeception.com](http://codeception.com/docs/modules/WebDriver#seeInTitle){:target="_blank"}. + +Attribute|Type|Use|Description +---|---|---|--- +`userInput`|string|optional| String to be searched for within the current page title. +`stepKey`|string|required| A unique identifier of the action. +`skipReadiness`|boolean|optional| A flag to skip the readiness check. +`before`|string|optional| `stepKey` of action that must be executed next. +`after`|string|optional| `stepKey` of preceding action. + +#### Example + +```xml +<!-- Verify that the title of the current active window contains the text "Page Title". --> +<seeInTitle userInput="Page Title" stepKey="seeInTitle"/> +``` + +### seeLink + +See [seeLink docs on codeception.com](http://codeception.com/docs/modules/WebDriver#seeLink){:target="_blank"}. + +Attribute|Type|Use|Description +---|---|---|--- +`userInput`|string|optional| String to be searched for within the text of the link. +`url`|string|optional| Hyperlink to be searched. +`stepKey`|string|required| A unique identifier of the action. +`skipReadiness`|boolean|optional| A flag to skip the readiness check. +`before`|string|optional| `stepKey` of action that must be executed next. +`after`|string|optional| `stepKey` of preceding action. + +#### Example + +```xml +<!-- Verify that there is a hyperlink tag on the page with the text "External link". --> +<seeLink userInput="External link" stepKey="seeLink"/> +``` + +```xml +<!-- Verify that there is a hyperlink tag with the text "External link" and the `href` attribute of `/admin`. --> +<seeLink userInput="External link" url="/admin" stepKey="seeAdminLink"/> +``` + +### seeNumberOfElements + +See [seeNumberOfElements docs on codeception.com](http://codeception.com/docs/modules/WebDriver#seeNumberOfElements){:target="_blank"}. + +Attribute|Type|Use|Description +---|---|---|--- +`selector`|string|optional| The selector identifying the corresponding HTML element. +`userInput`|string|optional| Number of instances of the specified selector to be found. +`parameterArray`|string|optional| Array of parameters to be searched for within the selector. +`stepKey`|string|required| A unique identifier of the action. +`skipReadiness`|boolean|optional| A flag to skip the readiness check. +`before`|string|optional| `stepKey` of action that must be executed next. +`after`|string|optional| `stepKey` of preceding action. + +#### Examples + +```xml +<!-- Verify there are 10 `<div id="product" ... >...</div>` elements on the page. --> +<seeNumberOfElements userInput="10" selector="div.product" stepKey="seeTenProducts"/> +``` + +```xml +<!-- Verify there are between 5 and 10 `<div id="product" ... >...</div>` elements on the page. --> +<seeNumberOfElements userInput="[5, 10]" selector=".product" stepKey="seeFiveToTenProducts"/> +``` + +### seeOptionIsSelected + +See [seeOptionIsSelected docs on codeception.com](http://codeception.com/docs/modules/WebDriver#seeOptionIsSelected){:target="_blank"}. + +Attribute|Type|Use|Description +---|---|---|--- +`selector`|string|optional| The selector identifying the corresponding HTML element. +`userInput`|string|optional| The name of the option that should be selected. +`stepKey`|string|required| A unique identifier of the action. +`skipReadiness`|boolean|optional| A flag to skip the readiness check. +`before`|string|optional| `stepKey` of action that must be executed next. +`after`|string|optional| `stepKey` of preceding action. + +#### Example + +```xml +<!-- Verify that `<select id="myselect" ... >...</select>` has the option `option1` selected --> +<seeOptionIsSelected userInput="option1" selector="select#myselect" stepKey="seeOption1"/> +``` + +### selectOption + +See [selectOption docs on codeception.com](http://codeception.com/docs/modules/WebDriver#selectOption){:target="_blank"}. + +Attribute|Type|Use|Description +---|---|---|--- +`selector`|string|optional| The selector identifying the corresponding HTML element. +`userInput`|string|optional| The name of the option to be selected. +`parameterArray`|string|optional| Array of options to be selected. +`stepKey`|string|required| A unique identifier of the action. +`skipReadiness`|boolean|optional| A flag to skip the readiness check. +`before`|string|optional| `stepKey` of action that must be executed next. +`after`|string|optional| `stepKey` of preceding action. + +#### Example + +```xml +<!-- Select `option1` from `<select id="mySelect" ... >...</select>`. --> +<selectOption userInput="option1" selector="select#mySelect" stepKey="selectOption1"/> +``` + +### selectMultipleOptions + +Selects all given options in the given Magento drop-down element. + +Attribute|Type|Use|Description +---|---|---|--- +`filterSelector`|string|required| The selector for the text filter field. +`optionSelector`|string|required| The selector used to select the corresponding options based on the filter field. +`stepKey`|string|required| A unique identifier of the action. +`skipReadiness`|boolean|optional| A flag to skip the readiness check. +`before`|string|optional| `stepKey` of action that must be executed next. +`after`|string|optional| `stepKey` of preceding action. + +It contains a child element `<array>` where you specify the options that must be selected using an array format like `['opt1', 'opt2']`. + +#### Example + +```xml +<!-- Select the options `opt1` and `opt2` from `<option class="option" ... >...</option>` and `<input class="filter" ...>...</input>` --> +<selectMultipleOptions filterSelector=".filter" optionSelector=".option" stepKey="selectMultipleOpts1"> + <array>['opt1', 'opt2']</array> +</selectMultipleOptions> +``` + +### setCookie + +See [setCookie docs on codeception.com](http://codeception.com/docs/modules/WebDriver#setCookie){:target="_blank"}. + +Attribute|Type|Use|Description +---|---|---|--- +`userInput`|string|optional| The name of the cookie to be set. +`parameterArray`|string|optional| Array of name/value pairs to be set within the cookie. +`value`|string|optional| Value to be written to the cookie. +`stepKey`|string|required| A unique identifier of the action. +`skipReadiness`|boolean|optional| A flag to skip the readiness check. +`before`|string|optional| `stepKey` of action that must be executed next. +`after`|string|optional| `stepKey` of preceding action. + +#### Examples + +```xml +<!-- Set a cookie with the name of `cookieName` and value of `cookieValue`. --> +<setCookie userInput="cookieName" value="cookieValue" stepKey="setCookie"/> +``` + +### submitForm + +See [submitForm docs on codeception.com](http://codeception.com/docs/modules/WebDriver#submitForm){:target="_blank"}. + +Attribute|Type|Use|Description +---|---|---|--- +`selector`|string|optional| The selector identifying the corresponding HTML element. +`parameterArray`|string|optional| An array of form field names and their corresponding values. +`button`|string|optional| Selector for the form submit button. +`stepKey`|string|required| A unique identifier of the action. +`skipReadiness`|boolean|optional| A flag to skip the readiness check. +`before`|string|optional| `stepKey` of action that must be executed next. +`after`|string|optional| `stepKey` of preceding action. + +#### Examples + +```xml +<!-- Submit a value of `admin` for `<input name="username" ... >...</input>`, a value of `123123q` for `<input name="password" ... >...</input>` for the form `<form id="loginForm" ...>...</form>` and a submit button of `<button id="submit" ... >...</button>` --> +<submitForm selector="#loginForm" parameterArray="['username' => 'admin','password' => '123123q']" button="#submit" stepKey="submitForm"/> +``` + +### switchToIFrame + +See [switchToIFrame docs on codeception.com](http://codeception.com/docs/modules/WebDriver#switchToIFrame){:target="_blank"}. + +Attribute|Type|Use|Description +---|---|---|--- +`selector`|string|optional| The selector identifying the corresponding HTML element. +`userInput`|string|optional| The name of the IFrame to set focus to. +`stepKey`|string|required| A unique identifier of the action. +`skipReadiness`|boolean|optional| A flag to skip the readiness check. +`before`|string|optional| `stepKey` of action that must be executed next. +`after`|string|optional| `stepKey` of preceding action. + +#### Example + +```xml +<!-- Set the focus to <iframe name="embeddedFrame" ... /> --> +<switchToIFrame userInput="embeddedFrame" stepKey="switchToIFrame"/> +``` + +### switchToNextTab + +See [switchToNextTab docs on codeception.com](http://codeception.com/docs/modules/WebDriver#switchToNextTab){:target="_blank"}. + +Attribute|Type|Use|Description +---|---|---|--- +`userInput`|string|optional| Offset of the tab to open, usually a number. +`stepKey`|string|required| A unique identifier of the action. +`skipReadiness`|boolean|optional| A flag to skip the readiness check. +`before`|string|optional| `stepKey` of action that must be executed next. +`after`|string|optional| `stepKey` of preceding action. + +#### Examples + +```xml +<!-- Switch to the next tab. --> +<switchToNextTab stepKey="switchToNextTab"/> +``` + +```xml +<!-- Switch to the third next tab. --> +<switchToNextTab userInput="3" stepKey="switchToThirdNextTab"/> +``` + +### switchToPreviousTab + +See [switchToPreviousTab docs on codeception.com](http://codeception.com/docs/modules/WebDriver#switchToPreviousTab){:target="_blank"}. + +Attribute|Type|Use|Description +---|---|---|--- +`userInput`|string|optional| Number of tabs to go back. +`stepKey`|string|required| A unique identifier of the action. +`skipReadiness`|boolean|optional| A flag to skip the readiness check. +`before`|string|optional| `stepKey` of action that must be executed next. +`after`|string|optional| `stepKey` of preceding action. + +#### Examples + +```xml +<!-- Switch to the previous tab. --> +<switchToPreviousTab stepKey="switchToPreviousTab"/> +``` + +```xml +<!-- Switch to the third previous tab. --> +<switchToPreviousTab userInput="3" stepKey="switchToThirdPreviousTab"/> +``` + +### switchToWindow + +See [switchToWindow docs on codeception.com](http://codeception.com/docs/modules/WebDriver#switchToWindow){:target="_blank"}. + +Attribute|Type|Use|Description +---|---|---|--- +`userInput`|string|optional| The name of new window to be opened. +`stepKey`|string|required| A unique identifier of the action. +`skipReadiness`|boolean|optional| A flag to skip the readiness check. +`before`|string|optional| `stepKey` of action that must be executed next. +`after`|string|optional| `stepKey` of preceding action. + +#### Example + +```xml +<!-- Switch to a window with the `name` parameter of `newWindow`. --> +<switchToWindow userInput="newWindow" stepKey="switchToWindow"/> +``` + +### typeInPopup + +See [typeInPopup docs on codeception.com](http://codeception.com/docs/modules/WebDriver#typeInPopup){:target="_blank"}. + +Attribute|Type|Use|Description +---|---|---|--- +`userInput`|string|optional| String to be added to the current popup. +`stepKey`|string|required| A unique identifier of the action. +`skipReadiness`|boolean|optional| A flag to skip the readiness check. +`before`|string|optional| `stepKey` of action that must be executed next. +`after`|string|optional| `stepKey` of preceding action. + +#### Example + +```xml +<!-- Type the text "Sample Text" into the current popup visible on the page. --> +<typeInPopup userInput="Sample Text" stepKey="typeInPopup"/> +``` + +### uncheckOption + +See [uncheckOption docs on codeception.com](http://codeception.com/docs/modules/WebDriver#uncheckOption){:target="_blank"}. + +Attribute|Type|Use|Description +---|---|---|--- +`selector`|string|optional| The selector identifying the corresponding HTML element. +`stepKey`|string|required| A unique identifier of the action. +`skipReadiness`|boolean|optional| A flag to skip the readiness check. +`before`|string|optional| `stepKey` of action that must be executed next. +`after`|string|optional| `stepKey` of preceding action. + +#### Example + +```xml +<!-- Ensure the checkbox `<input type="checkbox" id="checkbox" ... >...</input>` is unchecked. --> +<uncheckOption selector="input#checkbox" stepKey="uncheckCheckbox"/> +``` + +### unselectOption + +See [unselectOption docs on codeception.com](http://codeception.com/docs/modules/WebDriver#unselectOption){:target="_blank"}. + +Attribute|Type|Use|Description +---|---|---|--- +`selector`|string|optional| The selector identifying the corresponding HTML element. +`userInput`|string|optional| The name of the option to deselect. +`parameterArray`|string|optional| Array of options to be deselected. +`stepKey`|string|required| A unique identifier of the action. +`skipReadiness`|boolean|optional| A flag to skip the readiness check. +`before`|string|optional| `stepKey` of action that must be executed next. +`after`|string|optional| `stepKey` of preceding action. + +#### Example + +```xml +<!-- Deselect `option1` from `<select id="mySelect" ... >...</select>`. --> +<unselectOption userInput="option1" selector="select#myselect" stepKey="unselectOption1"/> +``` + +### updateData + +When you create a data entity using `createData`, you may need to update it later in the test. +The `updateData` action allows this. + +For example, to change the price of a product: + +```xml +<updateData entity="AdjustPriceProduct" createDataKey="productHandle" stepKey="updateProduct"/> +``` + +Where `AdjustPriceProduct` simply looks like this: + +```xml +<entity name="AdjustPriceProduct" type="product"> + <data key="price">321.00</data> +</entity> +``` + +Only the fields that you want to update are set. + +Attribute|Type|Use|Description +---|---|---|--- +`storeCode`|string|optional| ID of the store in which to apply the updated data. +`entity`|string|required| The name of the `updateData` entity being created. +`createDataKey`|string|required| Key of the data entity to be updated. +`stepKey`|string|required| A unique identifier of the action. +`skipReadiness`|boolean|optional| A flag to skip the readiness check. +`before`|string|optional| `stepKey` of action that must be executed next. +`after`|string|optional| `stepKey` of preceding action. + +This action can optionally contain one or more [requiredEntity](#requiredentity) child elements. + +### wait + +See [wait docs on codeception.com](http://codeception.com/docs/modules/WebDriver#wait){:target="_blank"}. + +Attribute|Type|Use|Description +---|---|---|--- +`time`|string|optional| The number of seconds to wait. +`stepKey`|string|required| A unique identifier of the action. +`skipReadiness`|boolean|optional| A flag to skip the readiness check. +`before`|string|optional| `stepKey` of action that must be executed next. +`after`|string|optional| `stepKey` of preceding action. + +#### Example + +```xml +<!-- Halt test execution for 10 seconds before continuing. --> +<wait time="10" stepKey="waitTenSeconds"/> +``` + +### waitForAjaxLoad + +Wait for all AJAX calls to finish. + +Attribute|Type|Use|Description +---|---|---|--- +`time`|string|optional| The number of seconds to wait for Ajax calls to finish. +`stepKey`|string|required| A unique identifier of the action. +`skipReadiness`|boolean|optional| A flag to skip the readiness check. +`before`|string|optional| `stepKey` of action that must be executed next. +`after`|string|optional| `stepKey` of preceding action. + +#### Example + +```xml +<!-- Wait up to 30 seconds for all AJAX calls to finish before continuing. --> +<waitForAjaxLoad stepKey="waitForAjaxLoad"/> +``` + +### waitForElementChange + +See [waitForElementChange docs on codeception.com](http://codeception.com/docs/modules/WebDriver#waitForElementChange){:target="_blank"}. + +Attribute|Type|Use|Description +---|---|---|--- +`selector`|string|optional| The selector identifying the HTML element to be changed. +`function`|string|optional| The function to be run after the element changes. +`time`|string|optional| The number of seconds to wait for the change. Default is 30. +`stepKey`|string|required| A unique identifier of the action. +`skipReadiness`|boolean|optional| A flag to skip the readiness check. +`before`|string|optional| `stepKey` of action that must be executed next. +`after`|string|optional| `stepKey` of preceding action. + +#### Example + +```xml +<!-- Wait up to 30 seconds for `<div id="changedElement" ... >...</div>` to change to displayed before continuing. --> +<waitForElementChange selector="div#changedElement" function="function(\WebDriverElement $el) {return $el->isDisplayed();}" stepKey="waitForElementChange"/> +``` + +### waitForElement + +See [waitForElement docs on codeception.com](http://codeception.com/docs/modules/WebDriver#waitForElement){:target="_blank"}. + +Attribute|Type|Use|Description +---|---|---|--- +`selector`|string|optional| The selector identifying the corresponding HTML element. +`time`|string|optional| The number of seconds to wait for the element to appear. +`stepKey`|string|required| A unique identifier of the action. +`skipReadiness`|boolean|optional| A flag to skip the readiness check. +`before`|string|optional| `stepKey` of action that must be executed next. +`after`|string|optional| `stepKey` of preceding action. + +#### Example + +```xml +<!-- Wait up to 30 seconds for `<div id="changedElement" ... >...</div>` to be appear on the page before continuing. --> +<waitForElement selector="#changedElement" stepKey="waitForElement"/> +``` + +### waitForElementNotVisible + +See [waitForElementNotVisible docs on codeception.com](http://codeception.com/docs/modules/WebDriver#waitForElementNotVisible){:target="_blank"}. + +Attribute|Type|Use|Description +---|---|---|--- +`selector`|string|optional| The selector identifying the corresponding HTML element. +`time`|string|optional| The number of seconds to wait for the element to become not visible. +`stepKey`|string|required| A unique identifier of the action. +`skipReadiness`|boolean|optional| A flag to skip the readiness check. +`before`|string|optional| `stepKey` of action that must be executed next. +`after`|string|optional| `stepKey` of preceding action. + +#### Example + +```xml +<!-- Wait up to 30 seconds for `<div id="changedElement" ... >...</div>` to become non-visible on the page before continuing. --> +<waitForElementNotVisible selector="#changedElement" stepKey="waitForElementNotVisible"/> +``` + +### waitForElementVisible + +See [waitForElementVisible docs on codeception.com](http://codeception.com/docs/modules/WebDriver#waitForElementVisible){:target="_blank"}. + +Attribute|Type|Use|Description +---|---|---|--- +`selector`|string|optional| The selector identifying the corresponding HTML element. +`time`|string|optional| The number of seconds to wait for the element to appear. +`stepKey`|string|required| A unique identifier of the action. +`skipReadiness`|boolean|optional| A flag to skip the readiness check. +`before`|string|optional| `stepKey` of action that must be executed next. +`after`|string|optional| `stepKey` of preceding action. + +#### Example + +```xml +<!-- Wait up to 30 seconds for `<div id="changedElement" ... >...</div>` to become visible on the page before continuing. --> +<waitForElementVisible selector="#changedElement" stepKey="waitForElementVisible"/> +``` + +### waitForJS + +See [waitForJS docs on codeception.com](http://codeception.com/docs/modules/WebDriver#waitForJS){:target="_blank"}. + +Attribute|Type|Use|Description +---|---|---|--- +`function`|string|optional| The function to be run after all JavaScript finishes. +`time`|string|optional| The number of seconds to wait for JavaScript to finish. +`stepKey`|string|required| A unique identifier of the action. +`skipReadiness`|boolean|optional| A flag to skip the readiness check. +`before`|string|optional| `stepKey` of action that must be executed next. +`after`|string|optional| `stepKey` of preceding action. + +#### Example + +```xml +<!-- Wait for all jQuery AJAX requests to finish before continuing. --> +<waitForJS function="return $.active == 0;" stepKey="waitForJS"/> +``` + +### waitForLoadingMaskToDisappear + +Wait for all Magento loading overlays to disappear. + +{: .bs-callout .bs-callout-info } +The CSS class for loading masks is not used consistently throughout Magento. +Therefore, this convenience function tries to wait for various specific selectors. + +```config +# Wait for these classes to not be visible + +//div[contains(@class, "loading-mask")] +//div[contains(@class, "admin_data-grid-loading-mask")] +//div[contains(@class, "admin__data-grid-loading-mask")] +//div[contains(@class, "admin__form-loading-mask")] +//div[@data-role="spinner"] +``` + +Attribute|Type|Use|Description +---|---|---|--- +`stepKey`|string|required| A unique identifier of the action. +`skipReadiness`|boolean|optional| A flag to skip the readiness check. +`before`|string|optional| `stepKey` of action that must be executed next. +`after`|string|optional| `stepKey` of preceding action. + +#### Example + +```xml +<!-- Wait up to 30 seconds for all Magento loading overlays to disappear before continuing. --> +<waitForLoadingMaskToDisappear stepKey="waitForLoadingMaskToDisappear"/> +``` + +### waitForPageLoad + +Wait for AJAX, Magento loading overlays, and `document.readyState == "complete"`. + +Attribute|Type|Use|Description +---|---|---|--- +`time`|string|optional| Number of seconds to wait for the page to load. +`stepKey`|string|required| A unique identifier of the action. +`skipReadiness`|boolean|optional| A flag to skip the readiness check. +`before`|string|optional| `stepKey` of action that must be executed next. +`after`|string|optional| `stepKey` of preceding action. + +#### Example + +```xml +<!-- Wait up to 30 seconds for the current page to fully load before continuing. --> +<waitForPageLoad stepKey="waitForPageLoad"/> +``` +### waitForPwaElementNotVisible + +Waits up to the given `time` for a PWA Element to disappear from the screen. + +Attribute|Type|Use|Description +---|---|---|--- +`time`|string|optional| Number of seconds to wait for the element to disappear. +`selector`|string|required| The selector identifying the corresponding HTML element. +`stepKey`|string|required| A unique identifier of the action. +`skipReadiness`|boolean|optional| A flag to skip the readiness check. +`before`|string|optional| `stepKey` of action that must be executed next. +`after`|string|optional| `stepKey` of preceding action. + +#### Example + +```xml +<!-- Wait for the PWA element to disappear. --> +<waitForPwaElementNotVisible time="1" stepKey="waitForPwaElementNotVisible"/> +``` +### waitForPwaElementVisible + +Waits up to the given 'time' for a PWA Element to appear on the screen. + +Attribute|Type|Use|Description +---|---|---|--- +`time`|string|optional| Number of seconds to wait for the selected element. +`selector`|string|required| The selector identifying the corresponding HTML element. +`stepKey`|string|required| A unique identifier of the action. +`skipReadiness`|boolean|optional| A flag to skip the readiness check. +`before`|string|optional| `stepKey` of action that must be executed next. +`after`|string|optional| `stepKey` of preceding action. + +#### Example + +```xml +<!-- Wait for the selected element to appear. --> +<waitForPwaElementVisible stepKey="waitForPwaElementVisible"/> +``` + +### waitForText + +See [waitForText docs on codeception.com](http://codeception.com/docs/modules/WebDriver#waitForText){:target="_blank"}. + +Attribute|Type|Use|Description +---|---|---|--- +`userInput`|string|optional| The string to wait for. +`time`|string|optional| The number of seconds to wait for the text to appear. +`selector`|string|optional| The selector identifying the corresponding HTML element. +`stepKey`|string|required| A unique identifier of the action. +`skipReadiness`|boolean|optional| A flag to skip the readiness check. +`before`|string|optional| `stepKey` of action that must be executed next. +`after`|string|optional| `stepKey` of preceding action. + +#### Example + +```xml +<!-- Wait for text "Sample Text" to appear in the selected area before continuing. --> +<waitForText userInput="Sample Text" selector="div#page" stepKey="waitForText"/> +``` diff --git a/docs/test/annotations.md b/docs/test/annotations.md new file mode 100644 index 000000000..673db0e84 --- /dev/null +++ b/docs/test/annotations.md @@ -0,0 +1,237 @@ +--- +mftf-release: 2.3.6 +redirect_from: /guides/v2.3/magento-functional-testing-framework/2.3/test/annotations.html +--- + +# Annotations + +_This topic was updated due to the {{page.mftf-release}} MFTF release._ +{: style="text-align: right"} + +Annotations are essentially comments in the code. In PHP, they all are marked by a preceding `@` symbol. + +Within [tests], annotations are contained within their own node. + +## Principles + +The following conventions apply to annotations in the Magento Functional Testing Framework (MFTF): + +- All annotations are within an `<annotations>` element. +- Each element within corresponds to a supported annotation type. +- There is no distinction made in XML between Codeception annotations and Allure annotations. +- Each annotation contains only one value. +If multiple annotation values are supported and required each value requires a separate annotation. +- Tests must contain all of the following annotations: stories, title, description, severity. + +Recommended use cases of the annotation types: +- [stories] - report grouping, a set of tests that verify a story. +- [title] - description of the test purpose. +- [group] - general functionality grouping. +- [description] - description of how the test achieves the purpose defined in the title. +- [skip] - a label for the test to be skipped during generation (for example, an incomplete test blocked by an issue) + +## Example + +```xml +<annotations> + <stories value="Category Creation"/> + <title value="Create a Category via Admin"/> + <description value="Test logs into admin backend and creates a category."/> + <severity value="CRITICAL"/> + <group value="category"/> +</annotations> +``` + +## Reference + +### description + +The `<description>` element is an implementation of a [`@Description`] Allure tag; Metadata for report. + +Attribute|Type|Use +---|---|-- +`value`|string|required + +#### Example + +```xml +<description value="Add Catalog via Admin"/> +``` + +### features + +The `<features>` element is an implementation of a [`@Features`] Allure tag. + +`<features>` sets a string that will be displayed as a feature within the Allure report. Tests under the same feature are grouped together in the report. + +Attribute|Type|Use +---|---|-- +`value`|string|required + +#### Example + +```xml +<features value="Catalog"/> +<features value="Add/Edit"/> +``` + +### group + +The `<group>` element is an implementation of a [`@group`] Codeception tag. + +`<group>` specifies a string to identify and collect tests together. +Any test can be a part of multiple groups. +The purpose of grouping is to create a set of test for a functionality or purpose, such as all cart tests or all slow tests and run them together locally. + +{:.bs-callout .bs-callout-warning} +Group values cannot collide with [suite] names. + +{:.bs-callout .bs-callout-tip} +Add `<skip>` to the test to skip it during test run. + +Attribute|Type|Use|Definition +---|---|---|--- +`value`|string|required|A value that is used to group tests. It should be lower case. `skip` is reserved to ignore content of the test and generate an empty test. + +#### Example + +```xml +<group value="category"/> +``` + +### return + +The `<return>` element is an implementation of a [`@return`] Codeception tag. +It specifies what is returned from a test execution. + +Attribute|Type|Use +---|---|-- +`value`|string|required + +#### Example + +```xml +<return value="void"/> +``` + +### severity + +The `<return>` element is an implementation of a [`@Severity`] Allure tag; Metadata for report. + +Attribute|Type|Use|Acceptable values +---|---|---|--- +`value`|string|required|`MINOR`, `AVERAGE`, `MAJOR`, `BLOCKER`, `CRITICAL` + +#### Example + +```xml +<severity value="CRITICAL"/> +``` + +### skip + +Use the `<skip>` element to skip a test. +It contains one or more child elements `<issueId>` to specify one or more issues that cause the test skipping. + +##### issueId + +This element under `<skip>` is required at least once and contains references to issues that cause the test to be skipped. + +Attribute|Type|Use +---|---|-- +`value`|string|required + +#### Example + +```xml +<skip> + <issueId value="#117"/> + <issueId value="MC-345"/> +</skip> +``` + +### stories + +The `<stories>` element is an implementation of a [`@Stories`] Allure tag. +It has the same functionality as [features], within the Story report group. + +Attribute|Type|Use +---|---|-- +`value`|string|required + +#### Example + +```xml +<stories value="Add Catalog"/> +<stories value="Edit Catalog"/> +``` + +### testCaseId + +The `<testCaseId>` element is an implementation of a [`@TestCaseId`] Allure tag. +It specifies a ZephyrId for a test. + +This tag is prefixed to a title of the test annotation to make the test title unique in Allure. + +If the linkage is set up correctly in the Allure config, the test will have a hyperlink to the Zephyr test case in the report. + +Learn more about [setup instructions in Allure]. + +Attribute|Type|Use +---|---|-- +`value`|string|required + +#### Example + +```xml +<testCaseId value="#"/> +``` + +### title + +The `<title>` element is an implementation of [`@Title`] Allure tag; Metadata for report. + +Attribute|Type|Use +---|---|-- +`value`|string|required + +#### Example + +```xml +<title value="Add Catalog"/> +``` + +### useCaseId + +The `<useCaseId>` element is an implementation of a `@UseCaseId` custom tag. It specifies the use case ID for a test and is ignored by Allure configuration at the moment, as Allure implementation is not complete. + +Attribute|Type|Use +---|---|-- +`value`|string|required + +#### Example + +```xml +<useCaseId value="USECASE-1"/> +``` + +<!-- Link definitions --> + +[`@Description`]: https://devhub.io/zh/repos/allure-framework-allure-phpunit#extended-test-class-or-test-method-description +[`@Features`]: https://devhub.io/zh/repos/allure-framework-allure-phpunit#map-test-classes-and-test-methods-to-features-and-stories +[`@group`]: http://codeception.com/docs/07-AdvancedUsage#Groups +[`@return`]: http://codeception.com/docs/07-AdvancedUsage#Examples +[`@Severity`]: https://devhub.io/zh/repos/allure-framework-allure-phpunit#set-test-severity +[`@Stories`]: https://devhub.io/zh/repos/allure-framework-allure-phpunit#map-test-classes-and-test-methods-to-features-and-stories +[`@TestCaseId`]: https://github.com/allure-framework/allure1/wiki/Test-Case-ID +[`@Title`]: https://devhub.io/zh/repos/allure-framework-allure-phpunit#human-readable-test-class-or-test-method-title +[description]: #description +[features]: #features +[group]: #group +[setup instructions in Allure]: https://github.com/allure-framework/allure1/wiki/Test-Case-ID +[severity]: #severity +[stories]: #stories +[suite]: ../suite.html +[tests]: ../test.html +[title]: #title +[skip]: #skip diff --git a/docs/test/assertions.md b/docs/test/assertions.md new file mode 100644 index 000000000..c5c453273 --- /dev/null +++ b/docs/test/assertions.md @@ -0,0 +1,584 @@ +--- +mftf-release: 2.3.0 +redirect_from: /guides/v2.3/magento-functional-testing-framework/2.3/test/assertions.html +--- + +# Assertions + +_This topic was updated due to the {{page.mftf-release}} MFTF release._ +{: style="text-align: right"} + +Assertions serve to pass or fail the [test](../test.html#test-tag) if a condition is not met. These assertions will look familiar to you if you've used any other testing framework, like PHPUnit. + +All assertions contain the same [common actions attributes](./actions.html#common-attributes): `stepKey`, `before`, and `after`. + +Most assertions contain a `message` attribute that specifies the text of an informational message to help you identify the cause of the failure. + +## Principles + +The [principles for actions](../test.html#principles) are also applicable to assertions. + +Assertion actions have nested self-descriptive elements, `<expectedResult>` and `<actualResult>`. These elements contain a result type and a value: +* `type` + * `const` (default) + * `int` + * `float` + * `bool` + * `string` + * `variable` + * `array` +* `value` + +If `variable` is used, the test transforms the corresponding value to `$variable`. Use the `stepKey` of a test, that returns the value you want to use, in assertions: + +`actual="stepKeyOfGrab" actualType="variable"` + +To use variables embedded in a string in `expected` and `actual` of your assertion, use the `{$stepKey}` format: + +`actual="A long assert string {$stepKeyOfGrab} with an embedded variable reference." actualType="variable"` + +## Example + +The following example shows a common test that gets text from a page and asserts that it matches what we expect to see. If it does not, the test will fail at the assert step. + +```xml +<!-- Grab a value from the page using any grab action --> +<grabTextFrom selector="#elementId" stepKey="stepKeyOfGrab"/> + +<!-- Ensure that the value we grabbed matches our expectation --> +<assertEquals message="This is an optional human readable hint that will be shown in the logs if this assert fails." stepKey="assertEquals1"> + <expectedResult type="string">Some String</expectedResult> + <actualResult type="string">A long assert string {$stepKeyOfGrab} with an embedded variable reference.</actualResult> +</assertEquals> +``` + +## Elements reference + +### assertElementContainsAttribute + +Example: + +```xml +<assertElementContainsAttribute selector=".admin__menu-overlay" attribute="style" expectedValue="color: #333;" stepKey="assertElementContainsAttribute"/> +``` + +Attribute|Type|Use|Description +---|---|---|--- +`selector`|string|required| +`expectedValue`|string|optional| A value of the expected result. +`attribute`|string|required| +`stepKey`|string|required| A unique identifier of the text step. +`before`|string|optional| `stepKey` of action that must be executed next. +`after`|string|optional| `stepKey` of the preceding action. + +### assertArrayIsSorted + +The `<assertArrayIsSorted>` asserts that the array is sorted according to a specified sort order, ascending or descending. + +Example: + +```xml +<assertArrayIsSorted sortOrder="asc" stepKey="assertSorted"> + <array>[1,2,3,4,5,6,7]</array> +</assertArrayIsSorted> +``` + +Attribute|Type|Use|Description +---|---|---|--- +`sortOrder`|Possible values: `asc`, `desc`|required| A sort order to assert on array values. +`stepKey`|string|required| A unique identifier of the test step. +`before`|string|optional| `stepKey` of action that must be executed next. +`after`|string|optional| `stepKey` of the preceding action. + +It contains an `<array>` child element that specifies an array to be asserted for proper sorting. +It must be in typical array format like `[1,2,3,4,5]` or `[alpha, brontosaurus, zebra]`. + +### assertArrayHasKey + +See [assertArrayHasKey docs on codeception.com](http://codeception.com/docs/modules/Asserts#assertArrayHasKey){:target='_blank'} + +Attribute|Type|Use|Description +---|---|---|--- +`expected`|string|required| A value of the expected result. +`expectedType`|string|optional| A type of the expected result. Possible values: `const` (default), `int`, `float`, `bool`, `string`, `variable`, `array`. +`actual`|string|required| A value of the actual result. +`actualType`|string|optional| A type of the actual result. Possible values: `const` (default), `int`, `float`, `bool`, `string`, `variable`, `array`. +`message`|string|optional|Text of informational message about a cause of failure. +`stepKey`|string|required| A unique identifier of the text step. +`before`|string|optional| `stepKey` of action that must be executed next. +`after`|string|optional| `stepKey` of the preceding action. + +### assertArrayNotHasKey + +See [assertArrayNotHasKey docs on codeception.com](http://codeception.com/docs/modules/Asserts#assertArrayNotHasKey){:target='_blank'}. + +Attribute|Type|Use|Description +---|---|---|--- +`expected`|string|required| A value of the expected result. +`expectedType`|string|optional| A type of the expected result. Possible values: `const` (default), `int`, `float`, `bool`, `string`, `variable`, `array`. +`actual`|string|required| A value of the actual result. +`actualType`|string|optional| A type of the actual result. Possible values: `const` (default), `int`, `float`, `bool`, `string`, `variable`, `array`. +`message`|string|optional|Text of informational message about a cause of failure. +`stepKey`|string|required| A unique identifier of the text step. +`before`|string|optional| `stepKey` of action that must be executed next. +`after`|string|optional| `stepKey` of the preceding action. + +### assertArraySubset + +See [assertArraySubset docs on codeception.com](http://codeception.com/docs/modules/Asserts#assertArraySubset){:target='_blank'}. + +Attribute|Type|Use|Description +---|---|---|--- +`expected`|string|required| A value of the expected result. +`expectedType`|string|optional| A type of the expected result. Possible values: `const` (default), `int`, `float`, `bool`, `string`, `variable`, `array`. +`actual`|string|required| A value of the actual result. +`actualType`|string|optional| A type of the actual result. Possible values: `const` (default), `int`, `float`, `bool`, `string`, `variable`, `array`. +`strict`|boolean|optional| +`message`|string|optional|Text of informational message about a cause of failure. +`stepKey`|string|required| A unique identifier of the text step. +`before`|string|optional| `stepKey` of action that must be executed next. +`after`|string|optional| `stepKey` of the preceding action. + +### assertContains + +See [assertContains docs on codeception.com](http://codeception.com/docs/modules/Asserts#assertContains){:target='_blank'}. + +Attribute|Type|Use|Description +---|---|---|--- +`expected`|string|required| A value of the expected result. +`expectedType`|string|optional| A type of the expected result. Possible values: `const` (default), `int`, `float`, `bool`, `string`, `variable`, `array`. +`actual`|string|required| A value of the actual result. +`actualType`|string|optional| A type of the actual result. Possible values: `const` (default), `int`, `float`, `bool`, `string`, `variable`, `array`. +`message`|string|optional|Text of informational message about a cause of failure. +`stepKey`|string|required| A unique identifier of the text step. +`before`|string|optional| `stepKey` of action that must be executed next. +`after`|string|optional| `stepKey` of the preceding action. + +### assertCount + +See [assertCount docs on codeception.com](http://codeception.com/docs/modules/Asserts#assertCount){:target='_blank'}. + +Attribute|Type|Use|Description +---|---|---|--- +`expected`|string|required| A value of the expected result. +`expectedType`|string|optional| A type of the expected result. Possible values: `const` (default), `int`, `float`, `bool`, `string`, `variable`, `array`. +`actual`|string|required| A value of the actual result. +`actualType`|string|optional| A type of the actual result. Possible values: `const` (default), `int`, `float`, `bool`, `string`, `variable`, `array`. +`message`|string|optional|Text of informational message about a cause of failure. +`stepKey`|string|required| A unique identifier of the text step. +`before`|string|optional| `stepKey` of action that must be executed next. +`after`|string|optional| `stepKey` of the preceding action. + +### assertEmpty + +See [assertEmpty docs on codeception.com](http://codeception.com/docs/modules/Asserts#assertEmpty){:target='_blank'}. + +Attribute|Type|Use|Description +---|---|---|--- +`actual`|string|required| A value of the actual result. +`actualType`|string|optional| A type of the actual result. Possible values: `const` (default), `int`, `float`, `bool`, `string`, `variable`, `array`. +`message`|string|optional|Text of informational message about a cause of failure. +`stepKey`|string|required| A unique identifier of the text step. +`before`|string|optional| `stepKey` of action that must be executed next. +`after`|string|optional| `stepKey` of the preceding action. + +### assertEquals + +See [assertEquals docs on codeception.com](http://codeception.com/docs/modules/Asserts#assertEquals){:target='_blank'}. + +Attribute|Type|Use|Description +---|---|---|--- +`expected`|string|required| A value of the expected result. +`expectedType`|string|optional| A type of the expected result. Possible values: `const` (default), `int`, `float`, `bool`, `string`, `variable`, `array`. +`actual`|string|required| A value of the actual result. +`actualType`|string|optional| A type of the actual result. Possible values: `const` (default), `int`, `float`, `bool`, `string`, `variable`, `array`. +`delta`|string|optional| +`message`|string|optional|Text of informational message about a cause of failure. +`stepKey`|string|required| A unique identifier of the text step. +`before`|string|optional| `stepKey` of action that must be executed next. +`after`|string|optional| `stepKey` of the preceding action. + +### assertFalse + +See [assertFalse docs on codeception.com](http://codeception.com/docs/modules/Asserts#assertFalse){:target='_blank'}. + +Attribute|Type|Use|Description +---|---|---|--- +`actual`|string|required| Actual value. +`actualType`|assertEnum|optional| Type of actual value. +`message`|string|optional|Text of informational message about a cause of failure. +`stepKey`|string|required| A unique identifier of the text step. +`before`|string|optional| `stepKey` of action that must be executed next. +`after`|string|optional| `stepKey` of the preceding action. + +### assertFileExists + +See [assertFileExists docs on codeception.com](http://codeception.com/docs/modules/Asserts#assertFileExists){:target='_blank'}. + +Attribute|Type|Use|Description +---|---|---|--- +`actual`|string|required| A value of the actual result. +`actualType`|string|optional| A type of the actual result. Possible values: `const` (default), `int`, `float`, `bool`, `string`, `variable`, `array`. +`message`|string|optional|Text of informational message about a cause of failure. +`stepKey`|string|required| A unique identifier of the text step. +`before`|string|optional| `stepKey` of action that must be executed next. +`after`|string|optional| `stepKey` of the preceding action. + +### assertFileNotExists + +See [assertFileNotExists docs on codeception.com](http://codeception.com/docs/modules/Asserts#assertFileNotExists){:target='_blank'}. + +Attribute|Type|Use|Description +---|---|---|--- +`actual`|string|required| A value of the actual result. +`actualType`|string|optional| A type of the actual result. Possible values: `const` (default), `int`, `float`, `bool`, `string`, `variable`, `array`. +`message`|string|optional|Text of informational message about a cause of failure. +`stepKey`|string|required| A unique identifier of the text step. +`before`|string|optional| `stepKey` of action that must be executed next. +`after`|string|optional| `stepKey` of the preceding action. + +### assertGreaterOrEquals + +See [assertGreaterOrEquals docs on codeception.com](http://codeception.com/docs/modules/Asserts#assertGreaterOrEquals){:target='_blank'}. + +Attribute|Type|Use|Description +---|---|---|--- +`expected`|string|required| A value of the expected result. +`expectedType`|string|optional| A type of the expected result. Possible values: `const` (default), `int`, `float`, `bool`, `string`, `variable`, `array`. +`actual`|string|required| A value of the actual result. +`actualType`|string|optional| A type of the actual result. Possible values: `const` (default), `int`, `float`, `bool`, `string`, `variable`, `array`. +`message`|string|optional|Text of informational message about a cause of failure. +`stepKey`|string|required| A unique identifier of the text step. +`before`|string|optional| `stepKey` of action that must be executed next. +`after`|string|optional| `stepKey` of the preceding action. + +### assertGreaterThan + +See [assertGreaterThan docs on codeception.com](http://codeception.com/docs/modules/Asserts#assertGreaterThan){:target='_blank'}. + +Attribute|Type|Use|Description +---|---|---|--- +`expected`|string|required| A value of the expected result. +`expectedType`|string|optional| A type of the expected result. Possible values: `const` (default), `int`, `float`, `bool`, `string`, `variable`, `array`. +`actual`|string|required| A value of the actual result. +`actualType`|string|optional| A type of the actual result. Possible values: `const` (default), `int`, `float`, `bool`, `string`, `variable`, `array`. +`message`|string|optional|Text of informational message about a cause of failure. +`stepKey`|string|required| A unique identifier of the text step. +`before`|string|optional| `stepKey` of action that must be executed next. +`after`|string|optional| `stepKey` of the preceding action. + +### assertGreaterThanOrEqual + +See [assertGreaterThanOrEqual docs on codeception.com](http://codeception.com/docs/modules/Asserts#assertGreaterThanOrEqual){:target='_blank'}. + +Attribute|Type|Use|Description +---|---|---|--- +`expected`|string|required| A value of the expected result. +`expectedType`|string|optional| A type of the expected result. Possible values: `const` (default), `int`, `float`, `bool`, `string`, `variable`, `array`. +`actual`|string|required| A value of the actual result. +`actualType`|string|optional| A type of the actual result. Possible values: `const` (default), `int`, `float`, `bool`, `string`, `variable`, `array`. +`message`|string|optional|Text of informational message about a cause of failure. +`stepKey`|string|required| A unique identifier of the text step. +`before`|string|optional| `stepKey` of action that must be executed next. +`after`|string|optional| `stepKey` of the preceding action. + +### assertInstanceOf + +See [assertInstanceOf docs on codeception.com](http://codeception.com/docs/modules/Asserts#assertInstanceOf){:target='_blank'}. + +Attribute|Type|Use|Description +---|---|---|--- +`expected`|string|required| A value of the expected result. +`expectedType`|string|optional| A type of the expected result. Possible values: `const` (default), `int`, `float`, `bool`, `string`, `variable`, `array`. +`actual`|string|required| A value of the actual result. +`actualType`|string|optional| A type of the actual result. Possible values: `const` (default), `int`, `float`, `bool`, `string`, `variable`, `array`. +`message`|string|optional|Text of informational message about a cause of failure. +`stepKey`|string|required| A unique identifier of the text step. +`before`|string|optional| `stepKey` of action that must be executed next. +`after`|string|optional| `stepKey` of the preceding action. + +### assertInternalType + +See [assertInternalType docs on codeception.com](http://codeception.com/docs/modules/Asserts#assertInternalType){:target='_blank'}. + +Attribute|Type|Use|Description +---|---|---|--- +`expected`|string|required| A value of the expected result. +`expectedType`|string|optional| A type of the expected result. Possible values: `const` (default), `int`, `float`, `bool`, `string`, `variable`, `array`. +`actual`|string|required| A value of the actual result. +`actualType`|string|optional| A type of the actual result. Possible values: `const` (default), `int`, `float`, `bool`, `string`, `variable`, `array`. +`message`|string|optional|Text of informational message about a cause of failure. +`stepKey`|string|required| A unique identifier of the text step. +`before`|string|optional| `stepKey` of action that must be executed next. +`after`|string|optional| `stepKey` of the preceding action. + +### assertIsEmpty + +See [assertIsEmpty docs on codeception.com](http://codeception.com/docs/modules/Asserts#assertIsEmpty){:target='_blank'}. + +Attribute|Type|Use|Description +---|---|---|--- +`actual`|string|required| A value of the actual result. +`actualType`|string|optional| A type of the actual result. Possible values: `const` (default), `int`, `float`, `bool`, `string`, `variable`, `array`. +`message`|string|optional|Text of informational message about a cause of failure. +`stepKey`|string|required| A unique identifier of the text step. +`before`|string|optional| `stepKey` of action that must be executed next. +`after`|string|optional| `stepKey` of the preceding action. + +### assertLessOrEquals + +See [assertLessOrEquals docs on codeception.com](http://codeception.com/docs/modules/Asserts#assertLessOrEquals){:target='_blank'}. + +Attribute|Type|Use|Description +---|---|---|--- +`expected`|string|required| A value of the expected result. +`expectedType`|string|optional| A type of the expected result. Possible values: `const` (default), `int`, `float`, `bool`, `string`, `variable`, `array`. +`actual`|string|required| A value of the actual result. +`actualType`|string|optional| A type of the actual result. Possible values: `const` (default), `int`, `float`, `bool`, `string`, `variable`, `array`. +`message`|string|optional|Text of informational message about a cause of failure. +`stepKey`|string|required| A unique identifier of the text step. +`before`|string|optional| `stepKey` of action that must be executed next. +`after`|string|optional| `stepKey` of the preceding action. + +### assertLessThan + +See [assertLessThan docs on codeception.com](http://codeception.com/docs/modules/Asserts#assertLessThan){:target='_blank'}. + +Attribute|Type|Use|Description +---|---|---|--- +`expected`|string|required| A value of the expected result. +`expectedType`|string|optional| A type of the expected result. Possible values: `const` (default), `int`, `float`, `bool`, `string`, `variable`, `array`. +`actual`|string|required| A value of the actual result. +`actualType`|string|optional| A type of the actual result. Possible values: `const` (default), `int`, `float`, `bool`, `string`, `variable`, `array`. +`message`|string|optional|Text of informational message about a cause of failure. +`stepKey`|string|required| A unique identifier of the text step. +`before`|string|optional| `stepKey` of action that must be executed next. +`after`|string|optional| `stepKey` of the preceding action. + +### assertLessThanOrEqual + +See [assertLessThanOrEqual docs on codeception.com](http://codeception.com/docs/modules/Asserts#assertLessThanOrEqual){:target='_blank'}. + +Attribute|Type|Use|Description +---|---|---|--- +`expected`|string|required| A value of the expected result. +`expectedType`|string|optional| A type of the expected result. Possible values: `const` (default), `int`, `float`, `bool`, `string`, `variable`, `array`. +`actual`|string|required| A value of the actual result. +`actualType`|string|optional| A type of the actual result. Possible values: `const` (default), `int`, `float`, `bool`, `string`, `variable`, `array`. +`message`|string|optional|Text of informational message about a cause of failure. +`stepKey`|string|required| A unique identifier of the text step. +`before`|string|optional| `stepKey` of action that must be executed next. +`after`|string|optional| `stepKey` of the preceding action. + +### assertNotContains + +See [assertNotContains docs on codeception.com](http://codeception.com/docs/modules/Asserts#assertNotContains){:target='_blank'}. + +Attribute|Type|Use|Description +---|---|---|--- +`expected`|string|required| A value of the expected result. +`expectedType`|string|optional| A type of the expected result. Possible values: `const` (default), `int`, `float`, `bool`, `string`, `variable`, `array`. +`actual`|string|required| A value of the actual result. +`actualType`|string|optional| A type of the actual result. Possible values: `const` (default), `int`, `float`, `bool`, `string`, `variable`, `array`. +`message`|string|optional|Text of informational message about a cause of failure. +`stepKey`|string|required| A unique identifier of the text step. +`before`|string|optional| `stepKey` of action that must be executed next. +`after`|string|optional| `stepKey` of the preceding action. + +### assertNotEmpty + +See [assertNotEmpty docs on codeception.com](http://codeception.com/docs/modules/Asserts#assertNotEmpty){:target='_blank'}. + +Attribute|Type|Use|Description +---|---|---|--- +`actual`|string|required| A value of the actual result. +`actualType`|string|optional| A type of the actual result. Possible values: `const` (default), `int`, `float`, `bool`, `string`, `variable`, `array`. +`message`|string|optional|Text of informational message about a cause of failure. +`stepKey`|string|required| A unique identifier of the text step. +`before`|string|optional| `stepKey` of action that must be executed next. +`after`|string|optional| `stepKey` of the preceding action. + +### assertNotEquals + +See [assertNotEquals docs on codeception.com](http://codeception.com/docs/modules/Asserts#assertNotEquals){:target='_blank'}. + +Attribute|Type|Use|Description +---|---|---|--- +`expected`|string|required| A value of the expected result. +`expectedType`|string|optional| A type of the expected result. Possible values: `const` (default), `int`, `float`, `bool`, `string`, `variable`, `array`. +`actual`|string|required| A value of the actual result. +`actualType`|string|optional| A type of the actual result. Possible values: `const` (default), `int`, `float`, `bool`, `string`, `variable`, `array`. +`delta`|string|optional| +`message`|string|optional|Text of informational message about a cause of failure. +`stepKey`|string|required| A unique identifier of the text step. +`before`|string|optional| `stepKey` of action that must be executed next. +`after`|string|optional| `stepKey` of the preceding action. + +### assertNotInstanceOf + +See [assertNotInstanceOf docs on codeception.com](http://codeception.com/docs/modules/Asserts#assertNotInstanceOf){:target='_blank'}. + +Attribute|Type|Use|Description +---|---|---|--- +`expected`|string|required| A value of the expected result. +`expectedType`|string|optional| A type of the expected result. Possible values: `const` (default), `int`, `float`, `bool`, `string`, `variable`, `array`. +`actual`|string|required| A value of the actual result. +`actualType`|string|optional| A type of the actual result. Possible values: `const` (default), `int`, `float`, `bool`, `string`, `variable`, `array`. +`message`|string|optional|Text of informational message about a cause of failure. +`stepKey`|string|required| A unique identifier of the text step. +`before`|string|optional| `stepKey` of action that must be executed next. +`after`|string|optional| `stepKey` of the preceding action. + +### assertNotNull + +See [assertNotNull docs on codeception.com](http://codeception.com/docs/modules/Asserts#assertNotNull){:target='_blank'}. + +Attribute|Type|Use|Description +---|---|---|--- +`actual`|string|required| A value of the actual result. +`actualType`|string|optional| A type of the actual result. Possible values: `const` (default), `int`, `float`, `bool`, `string`, `variable`, `array`. +`message`|string|optional|Text of informational message about a cause of failure. +`stepKey`|string|required| A unique identifier of the text step. +`before`|string|optional| `stepKey` of action that must be executed next. +`after`|string|optional| `stepKey` of the preceding action. + +### assertNotRegExp + +See [assertNotRegExp docs on codeception.com](http://codeception.com/docs/modules/Asserts#assertNotRegExp){:target='_blank'}. + +Attribute|Type|Use|Description +---|---|---|--- +`expected`|string|required| A value of the expected result. +`expectedType`|string|optional| A type of the expected result. Possible values: `const` (default), `int`, `float`, `bool`, `string`, `variable`, `array`. +`actual`|string|required| A value of the actual result. +`actualType`|string|optional| A type of the actual result. Possible values: `const` (default), `int`, `float`, `bool`, `string`, `variable`, `array`. +`message`|string|optional|Text of informational message about a cause of failure. +`stepKey`|string|required| A unique identifier of the text step. +`before`|string|optional| `stepKey` of action that must be executed next. +`after`|string|optional| `stepKey` of the preceding action. + +### assertNotSame + +See [assertNotSame docs on codeception.com](http://codeception.com/docs/modules/Asserts#assertNotSame){:target='_blank'}. + +Attribute|Type|Use|Description +---|---|---|--- +`expected`|string|required| A value of the expected result. +`expectedType`|string|optional| A type of the expected result. Possible values: `const` (default), `int`, `float`, `bool`, `string`, `variable`, `array`. +`actual`|string|required| A value of the actual result. +`actualType`|string|optional| A type of the actual result. Possible values: `const` (default), `int`, `float`, `bool`, `string`, `variable`, `array`. +`message`|string|optional|Text of informational message about a cause of failure. +`stepKey`|string|required| A unique identifier of the text step. +`before`|string|optional| `stepKey` of action that must be executed next. +`after`|string|optional| `stepKey` of the preceding action. + +### assertNull + +See [assertNull docs on codeception.com](http://codeception.com/docs/modules/Asserts#assertNull){:target='_blank'}. + +Attribute|Type|Use|Description +---|---|---|--- +`actual`|string|required| A value of the actual result. +`actualType`|string|optional| A type of the actual result. Possible values: `const` (default), `int`, `float`, `bool`, `string`, `variable`, `array`. +`message`|string|optional|Text of informational message about a cause of failure. +`stepKey`|string|required| A unique identifier of the text step. +`before`|string|optional| `stepKey` of action that must be executed next. +`after`|string|optional| `stepKey` of the preceding action. + +### assertRegExp + +See [assertRegExp docs on codeception.com](http://codeception.com/docs/modules/Asserts#assertRegExp){:target='_blank'}. + +Attribute|Type|Use|Description +---|---|---|--- +`expected`|string|required| A value of the expected result. +`expectedType`|string|optional| A type of the expected result. Possible values: `const` (default), `int`, `float`, `bool`, `string`, `variable`, `array`. +`actual`|string|required| A value of the actual result. +`actualType`|string|optional| A type of the actual result. Possible values: `const` (default), `int`, `float`, `bool`, `string`, `variable`, `array`. +`message`|string|optional|Text of informational message about a cause of failure. +`stepKey`|string|required| A unique identifier of the text step. +`before`|string|optional| `stepKey` of action that must be executed next. +`after`|string|optional| `stepKey` of the preceding action. + +### assertSame + +See [assertSame docs on codeception.com](http://codeception.com/docs/modules/Asserts#assertSame){:target='_blank'}. + +Attribute|Type|Use|Description +---|---|---|--- +`expected`|string|required| A value of the expected result. +`expectedType`|string|optional| A type of the expected result. Possible values: `const` (default), `int`, `float`, `bool`, `string`, `variable`, `array`. +`actual`|string|required| A value of the actual result. +`actualType`|string|optional| A type of the actual result. Possible values: `const` (default), `int`, `float`, `bool`, `string`, `variable`, `array`. +`message`|string|optional|Text of informational message about a cause of failure. +`stepKey`|string|required| A unique identifier of the text step. +`before`|string|optional| `stepKey` of action that must be executed next. +`after`|string|optional| `stepKey` of the preceding action. + +### assertStringStartsNotWith + +See [assertStringStartsNotWith docs on codeception.com](http://codeception.com/docs/modules/Asserts#assertStringStartsNotWith){:target='_blank'}. + +Attribute|Type|Use|Description +---|---|---|--- +`expected`|string|required| A value of the expected result. +`expectedType`|string|optional| A type of the expected result. Possible values: `const` (default), `int`, `float`, `bool`, `string`, `variable`, `array`. +`actual`|string|required| A value of the actual result. +`actualType`|string|optional| A type of the actual result. Possible values: `const` (default), `int`, `float`, `bool`, `string`, `variable`, `array`. +`message`|string|optional|Text of informational message about a cause of failure. +`stepKey`|string|required| A unique identifier of the text step. +`before`|string|optional| `stepKey` of action that must be executed next. +`after`|string|optional| `stepKey` of the preceding action. + +### assertStringStartsWith + +See [assertStringStartsWith docs on codeception.com](http://codeception.com/docs/modules/Asserts#assertStringStartsWith){:target='_blank'}. + +Attribute|Type|Use|Description +---|---|---|--- +`expected`|string|required| A value of the expected result. +`expectedType`|string|optional| A type of the expected result. Possible values: `const` (default), `int`, `float`, `bool`, `string`, `variable`, `array`. +`actual`|string|required| A value of the actual result. +`actualType`|string|optional| A type of the actual result. Possible values: `const` (default), `int`, `float`, `bool`, `string`, `variable`, `array`. +`message`|string|optional|Text of informational message about a cause of failure. +`stepKey`|string|required| A unique identifier of the text step. +`before`|string|optional| `stepKey` of action that must be executed next. +`after`|string|optional| `stepKey` of the preceding action. + +### assertTrue + +See [assertTrue docs on codeception.com](http://codeception.com/docs/modules/Asserts#assertTrue){:target='_blank'}. + +Attribute|Type|Use|Description +---|---|---|--- +`actual`|string|required| A value of the actual result. +`actualType`|string|optional| A type of the actual result. Possible values: `const` (default), `int`, `float`, `bool`, `string`, `variable`, `array`. +`message`|string|optional|Text of informational message about a cause of failure. +`stepKey`|string|required| A unique identifier of the text step. +`before`|string|optional| `stepKey` of action that must be executed next. +`after`|string|optional| `stepKey` of the preceding action. + +### expectException + +See [expectException docs on codeception.com](http://codeception.com/docs/modules/WebDriver#expectException){:target='_blank'}. + +Attribute|Type|Use|Description +---|---|---|--- +`expected`|string|required| A value of the expected result. +`expectedType`|string|optional| A type of the expected result. Possible values: `const` (default), `int`, `float`, `bool`, `string`, `variable`, `array`. +`actual`|string|required| A value of the actual result. +`actualType`|string|optional| A type of the actual result. Possible values: `const` (default), `int`, `float`, `bool`, `string`, `variable`, `array`. +`stepKey`|string|required| A unique identifier of the text step. +`before`|string|optional| `stepKey` of action that must be executed next. +`after`|string|optional| `stepKey` of the preceding action. + +### fail + +See [fail docs on codeception.com](http://codeception.com/docs/modules/WebDriver#fail){:target='_blank'}. + +Attribute|Type|Use|Description +---|---|---|--- +`message`|string|required| +`stepKey`|string|required| A unique identifier of the text step. +`before`|string|optional| `stepKey` of action that must be executed next. +`after`|string|optional| `stepKey` of the preceding action. diff --git a/docs/test/img/action-groups-dia.svg b/docs/test/img/action-groups-dia.svg new file mode 100644 index 000000000..853420d54 --- /dev/null +++ b/docs/test/img/action-groups-dia.svg @@ -0,0 +1,516 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<svg + xmlns:dc="http://purl.org/dc/elements/1.1/" + xmlns:cc="http://creativecommons.org/ns#" + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns:svg="http://www.w3.org/2000/svg" + xmlns="http://www.w3.org/2000/svg" + xmlns:xlink="http://www.w3.org/1999/xlink" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + width="643" + height="114" + version="1.1" + id="svg152" + sodipodi:docname="action-groups-dia.svg" + inkscape:version="0.92.2 (5c3e80d, 2017-08-06)"> + <metadata + id="metadata158"> + <rdf:RDF> + <cc:Work + rdf:about=""> + <dc:format>image/svg+xml</dc:format> + <dc:type + rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> + </cc:Work> + </rdf:RDF> + </metadata> + <defs + id="defs156" /> + <sodipodi:namedview + pagecolor="#ffffff" + bordercolor="#666666" + borderopacity="1" + objecttolerance="10" + gridtolerance="10" + guidetolerance="10" + inkscape:pageopacity="0" + inkscape:pageshadow="2" + inkscape:window-width="1920" + inkscape:window-height="1017" + id="namedview154" + showgrid="false" + inkscape:zoom="1.2492586" + inkscape:cx="149.25073" + inkscape:cy="58.965954" + inkscape:window-x="3278" + inkscape:window-y="-8" + inkscape:window-maximized="1" + inkscape:current-layer="svg152" /> + <a + href="actions.html" + title="any actions" + id="a3851"> + <g + id="g242"> + <path + d="M404 25 L498 25 L504 31 L504 41 L498 47 L404 47 L398 41 L398 31 Z" + style="fill:rgb(255,255,255);stroke:rgb(0,0,0);stroke-width:1" + id="path2" /> + <text + x="451" + y="36" + style="font-family:Arial;font-size:8pt;fill:rgb(0,0,0);font-weight:bold;text-anchor:middle;dominant-baseline:central" + id="text4">actionTypeTags</text> + <line + x1="401" + y1="44" + x2="404" + y2="41" + style="stroke:rgb(0,0,0);stroke-width:2" + id="line6" /> + <path + d="M404 41 L406 43 L407 38 L402 39 L404 41 Z" + style="fill:rgb(0,0,0)" + id="path8" /> + <rect + x="499" + y="31" + width="10" + height="10" + style="fill:rgb(255,255,255);stroke:rgb(0,0,0);stroke-width:1" + id="rect10" /> + <line + x1="501" + y1="36" + x2="507" + y2="36" + style="stroke:rgb(0,0,0);stroke-width:1" + id="line12" /> + <line + x1="504" + y1="33" + x2="504" + y2="39" + style="stroke:rgb(0,0,0);stroke-width:1" + id="line14" /> + </g> + </a> + <a + id="a3924" + href="#argument-tag" + title="zero or more <argument> elements"> + <g + id="g233"> + <rect + x="566" + y="80" + width="70" + height="22" + style="fill:#ffffff;stroke:#000000;stroke-width:1;stroke-dasharray:4, 1" + id="rect16" /> + <rect + x="563" + y="77" + width="70" + height="22" + style="fill:#ffffff;stroke:#000000;stroke-width:1;stroke-dasharray:4, 1" + id="rect18" /> + <text + x="598" + y="88" + style="font-weight:bold;font-size:10.66666698px;font-family:Arial;dominant-baseline:central;text-anchor:middle;fill:#000000" + id="text20">argument</text> + <text + x="623" + y="109" + style="font-size:9.59999943px;font-family:Arial;dominant-baseline:central;text-anchor:end;fill:#000000" + id="text22">0..∞</text> + </g> + </a> + <line + x1="543" + y1="88" + x2="563" + y2="88" + style="stroke:rgb(0,0,0);stroke-width:1;stroke-linecap:round" + id="line24" /> + <a + id="a3914" + title="contains a sequence of"> + <g + id="g227"> + <path + d="M509 78 L532 78 L538 84 L538 92 L532 98 L509 98 L503 92 L503 84 Z" + style="fill:rgb(255,255,255);stroke:rgb(0,0,0);stroke-width:1" + id="path26" /> + <line + x1="506" + y1="88" + x2="535" + y2="88" + style="stroke:rgb(0,0,0);stroke-width:1" + id="line28" /> + <ellipse + cx="515" + cy="88" + rx="2" + ry="2" + style="rgb(0,0,0)" + id="ellipse30" /> + <ellipse + cx="520" + cy="88" + rx="2" + ry="2" + style="rgb(0,0,0)" + id="ellipse32" /> + <ellipse + cx="525" + cy="88" + rx="2" + ry="2" + style="rgb(0,0,0)" + id="ellipse34" /> + <rect + x="533" + y="83" + width="10" + height="10" + style="fill:rgb(255,255,255);stroke:rgb(0,0,0);stroke-width:1" + id="rect36" /> + <line + x1="535" + y1="88" + x2="541" + y2="88" + style="stroke:rgb(0,0,0);stroke-width:1" + id="line38" /> + </g> + </a> + <line + x1="483" + y1="88" + x2="503" + y2="88" + style="stroke:rgb(0,0,0);stroke-width:1;stroke-linecap:round" + id="line40" /> + <a + id="a3907" + href="#arguments-tag" + title="wrapping element <arguments>"> + <g + id="g218"> + <rect + x="398" + y="77" + width="80" + height="22" + style="fill:#ffffff;stroke:#000000;stroke-width:1" + id="rect42" /> + <text + x="438" + y="88" + style="font-weight:bold;font-size:10.66666698px;font-family:Arial;dominant-baseline:central;text-anchor:middle;fill:#000000" + id="text44">arguments</text> + <rect + x="473" + y="83" + width="10" + height="10" + style="fill:#ffffff;stroke:#000000;stroke-width:1" + id="rect46" /> + <line + x1="475" + y1="88" + x2="481" + y2="88" + style="stroke:#000000;stroke-width:1" + id="line48" /> + </g> + </a> + <line + x1="388" + y1="36" + x2="398" + y2="36" + style="stroke:rgb(0,0,0);stroke-width:1;stroke-linecap:round" + id="line50" /> + <line + x1="388" + y1="88" + x2="398" + y2="88" + style="stroke:rgb(0,0,0);stroke-width:1;stroke-linecap:round" + id="line52" /> + <line + x1="388" + y1="36" + x2="388" + y2="88" + style="stroke:rgb(0,0,0);stroke-width:1;stroke-linecap:round" + id="line54" /> + <a + id="a3833" + title="optionally contains muiliple times one of the following nodes"> + <g + id="g212"> + <line + x1="378" + y1="62" + x2="388" + y2="62" + style="stroke:rgb(0,0,0);stroke-width:1;stroke-linecap:round" + id="line56" /> + <path + d="M347 55 L370 55 L376 61 L376 69 L370 75 L347 75 L341 69 L341 61 Z" + style="fill:rgb(255,255,255);stroke:rgb(0,0,0);stroke-width:1;stroke-dasharray:4,1" + id="path58" /> + <path + d="M344 52 L367 52 L373 58 L373 66 L367 72 L344 72 L338 66 L338 58 Z" + style="fill:rgb(255,255,255);stroke:rgb(0,0,0);stroke-width:1;stroke-dasharray:4,1" + id="path60" /> + <line + x1="343" + y1="62" + x2="347" + y2="62" + style="stroke:rgb(0,0,0);stroke-width:1" + id="line62" /> + <line + x1="347" + y1="62" + x2="351" + y2="58" + style="stroke:rgb(0,0,0);stroke-width:1" + id="line64" /> + <line + x1="359" + y1="58" + x2="363" + y2="58" + style="stroke:rgb(0,0,0);stroke-width:1" + id="line66" /> + <line + x1="359" + y1="62" + x2="367" + y2="62" + style="stroke:rgb(0,0,0);stroke-width:1" + id="line68" /> + <line + x1="359" + y1="66" + x2="363" + y2="66" + style="stroke:rgb(0,0,0);stroke-width:1" + id="line70" /> + <line + x1="363" + y1="58" + x2="363" + y2="66" + style="stroke:rgb(0,0,0);stroke-width:1" + id="line72" /> + <ellipse + cx="355" + cy="58" + rx="2" + ry="2" + style="rgb(0,0,0)" + id="ellipse74" /> + <ellipse + cx="355" + cy="62" + rx="2" + ry="2" + style="rgb(0,0,0)" + id="ellipse76" /> + <ellipse + cx="355" + cy="66" + rx="2" + ry="2" + style="rgb(0,0,0)" + id="ellipse78" /> + <text + x="368" + y="82" + style="font-family:Arial;font-size:7.2pt;fill:rgb(0,0,0);text-anchor:end;dominant-baseline:central" + id="text80">0..∞</text> + <rect + x="368" + y="57" + width="10" + height="10" + style="fill:rgb(255,255,255);stroke:rgb(0,0,0);stroke-width:1" + id="rect82" /> + <line + x1="370" + y1="62" + x2="376" + y2="62" + style="stroke:rgb(0,0,0);stroke-width:1" + id="line84" /> + </g> + </a> + <line + x1="318" + y1="62" + x2="338" + y2="62" + style="stroke:rgb(0,0,0);stroke-width:1;stroke-linecap:round" + id="line86" /> + <a + id="a82" + href="#actiongroup-tag" + title="one or more <actionGroup> elements"> + <g + id="g195"> + <rect + x="227" + y="54" + width="89" + height="22" + style="fill:#ffffff;stroke:#000000;stroke-width:1" + id="rect88" /> + <rect + x="224" + y="51" + width="89" + height="22" + style="fill:#ffffff;stroke:#000000;stroke-width:1" + id="rect90" /> + <text + x="268.5" + y="62" + style="font-weight:bold;font-size:10.66666698px;font-family:Arial;dominant-baseline:central;text-anchor:middle;fill:#000000" + id="text92">actionGroup</text> + <text + x="308" + y="83" + style="font-size:9.59999943px;font-family:Arial;dominant-baseline:central;text-anchor:end;fill:#000000" + id="text94">1..∞</text> + <rect + x="308" + y="57" + width="10" + height="10" + style="fill:#ffffff;stroke:#000000;stroke-width:1" + id="rect96" /> + <line + x1="310" + y1="62" + x2="316" + y2="62" + style="stroke:#000000;stroke-width:1" + id="line98" /> + </g> + </a> + <line + x1="204" + y1="62" + x2="224" + y2="62" + style="stroke:rgb(0,0,0);stroke-width:1;stroke-linecap:round" + id="line100" /> + <line + x1="146.40559" + y1="62" + x2="166.40559" + y2="62" + style="stroke:#000000;stroke-width:1;stroke-linecap:round" + id="line126" /> + <a + id="a106" + title="Root element <actionGroups>" + href="#actiongroups-tag"> + <g + transform="translate(-10)" + id="g104"> + <line + x1="124.97701" + y1="62" + x2="146.40559" + y2="62" + style="stroke:#000000;stroke-width:1.03509831;stroke-linecap:round" + id="line142" /> + <rect + x="59.977016" + y="51" + width="95.357147" + height="22" + style="fill:#ffffff;stroke:#000000;stroke-width:1.03509831" + id="rect144" /> + <text + x="102.44111" + y="63.59021" + style="font-weight:bold;font-size:11.041049px;font-family:Arial;dominant-baseline:central;text-anchor:middle;fill:#000000;stroke-width:1.03509831" + id="text146" + transform="scale(1.0350984,0.96609173)">actionGroups</text> + <rect + x="147.83417" + y="57" + width="10.714286" + height="10" + style="fill:#ffffff;stroke:#000000;stroke-width:1.03509831" + id="rect148" /> + <line + x1="149.97702" + y1="62" + x2="156.40558" + y2="62" + style="stroke:#000000;stroke-width:1.03509831" + id="line150" /> + </g> + </a> + <a + id="a3823" + title="contains a sequence of"> + <g + id="g227-6" + transform="translate(-339.59441,-26)"> + <path + d="m 509,78 h 23 l 6,6 v 8 l -6,6 h -23 l -6,-6 v -8 z" + style="fill:#ffffff;stroke:#000000;stroke-width:1" + id="path26-0" + inkscape:connector-curvature="0" /> + <line + x1="506" + y1="88" + x2="535" + y2="88" + style="stroke:#000000;stroke-width:1" + id="line28-3" /> + <circle + cx="515" + cy="88" + id="ellipse30-8" + r="2" /> + <circle + cx="520" + cy="88" + id="ellipse32-3" + r="2" /> + <circle + cx="525" + cy="88" + id="ellipse34-4" + r="2" /> + <rect + x="533" + y="83" + width="10" + height="10" + style="fill:#ffffff;stroke:#000000;stroke-width:1" + id="rect36-0" /> + <line + x1="535" + y1="88" + x2="541" + y2="88" + style="stroke:#000000;stroke-width:1" + id="line38-2" /> + </g> + </a> +</svg> diff --git a/docs/tips-tricks.md b/docs/tips-tricks.md new file mode 100644 index 000000000..9ba09f29b --- /dev/null +++ b/docs/tips-tricks.md @@ -0,0 +1,382 @@ +--- +title: MFTF Tips and tricks +--- + +Sometimes, little changes can make a big difference in your project. Here are some test writing tips to keep everything running smoothly. + +<!-- {% raw %} --> + +## Actions and action groups + +### Use parameterized selectors in action groups with argument references + +Clarity and readability are important factors in good test writing. +Having to parse through unreadable code can be time consuming. Save time by writing clearly. +The good example clearly shows what the selector arguments refer to. +In the bad example we see two parameters being passed into the selector with little clue as to their purpose. + +**Why?** The next person maintaining the test or extending it may not be able to understand what the parameters are referencing. + +{:style="color:green"} +Good + +```xml +<test> + <actionGroup ref="VerifyOptionInProductStorefront" stepKey="verifyConfigurableOption" after="AssertProductInStorefrontProductPage"> + <argument name="attributeCode" value="$createConfigProductAttribute.default_frontend_label$"/> + <argument name="optionName" value="$createConfigProductAttributeOption1.option[store_labels][1][label]$"/> + </actionGroup> +</test> + +<actionGroup name="VerifyOptionInProductStorefront"> + <arguments> + <argument name="attributeCode" type="string"/> + <argument name="optionName" type="string"/> + </arguments> + <seeElement selector="{{StorefrontProductInfoMainSection.attributeOptionByAttributeID(attributeCode, optionName)}}" stepKey="verifyOptionExists"/> +</actionGroup> +``` + +{:style="color:red"} +Bad + +```xml +<test> + <seeElement selector="{{StorefrontProductInfoMainSection.attributeOptionByAttributeID($createConfigProductAttribute.default_frontend_label$, $createConfigProductAttributeOption1.option[store_labels][1][label]$)}}" stepKey="verifyOptionExists"/> +</test> +``` + +### Perform the most critical actions first in the `<after>` block + +Perform non-browser driving actions first. These are more likely to succeed as no UI is involved. +In the good example, `magentoCLI` and `deleteData` are run first to ensure a proper state. +In the bad example, we perform some heavy UI steps first. + +**Why?** If something goes wrong there, then the critical `magentoCLI` commands may not get a chance to run, leaving Magento configured incorrectly for any upcoming tests. + +{:style="color:green"} +Good: + +```xml +<after> + <magentoCLI command="indexer:set-mode" arguments="schedule" stepKey="setIndexerMode"/> + <magentoCLI command="config:set catalog/frontend/flat_catalog_category 0" stepKey="setFlatCatalogCategory"/> + <deleteData createDataKey="category" stepKey="deleteCategory"/> + <deleteData createDataKey="createSimpleProduct" stepKey="deleteSimpleProduct"/> + <actionGroup ref="AdminDeleteStoreViewActionGroup" stepKey="deleteStoreViewEn"> + <argument name="customStore" value="customStoreEN"/> + </actionGroup> + <actionGroup ref="AdminDeleteStoreViewActionGroup" stepKey="deleteStoreViewFr"> + <argument name="customStore" value="customStoreFR"/> + </actionGroup> + <actionGroup ref="logout" stepKey="logout"/> +</after> +``` + +{:style="color:red"} +Bad: + +```xml +<after> + <actionGroup ref="AdminDeleteStoreViewActionGroup" stepKey="deleteStoreViewEn"> + <argument name="customStore" value="customStoreEN"/> + </actionGroup> + <actionGroup ref="AdminDeleteStoreViewActionGroup" stepKey="deleteStoreViewFr"> + <argument name="customStore" value="customStoreFR"/> + </actionGroup> + <deleteData createDataKey="category" stepKey="deleteCategory"/> + <deleteData createDataKey="createSimpleProduct" stepKey="deleteSimpleProduct"/> + <magentoCLI command="config:set catalog/frontend/flat_catalog_category 0" stepKey="setFlatCatalogCategory"/> + <magentoCLI command="indexer:set-mode" arguments="schedule" stepKey="setIndexerMode"/> + <actionGroup ref="logout" stepKey="logout"/> +</after> +``` + +### When to use see vs. seeElement + +Use `see` and `seeElement` wisely. +If you need to see some element and verify that the text inside is shown correctly, use the `see` action. +If you need to verify that element present on page, use `seeElement`. +But never use `seeElement` and build a xPath which contains the expected text. + +**Why?** For `see` it will output something similar to this: +`Failed asserting that any element by #some_selector contains text "some_text"` +And for `seeElement` it will output something like this: +`Element by #some_selector is not visible.` +There is a subtle distinction: The first is a failure but it is the desired result: a 'positive failure'. +The second is a proper result of the action. + +{:style="color:green"} +Good: + +```xml +<see selector="//div[@data-element='content']//p" userInput="SOME EXPECTED TEXT" stepKey="seeSlide1ContentStorefront"/> +``` + +{:style="color:red"} +Bad: + +```xml +<seeElement selector="//div[@data-element='content']//p[.='SOME EXPECTED TEXT']" stepKey="seeSlide1ContentStorefront"/> +``` + +### Always specify a default value for action group arguments + +Whenever possible, specify a `defaultValue` for action group arguments. + +{:style="color:green"} +GOOD: + +```xml +<actionGroup name="StorefrontAssertProductImagesOnProductPageActionGroup"> + <arguments> + <argument name="productImage" type="string" defaultValue="Magento_Catalog/images/product/placeholder/image.jpg" /> + </arguments> + <waitForElementNotVisible selector="{{StorefrontProductMediaSection.gallerySpinner}}" stepKey="waitGallerySpinnerDisappear" /> + <seeElement selector="{{StorefrontProductMediaSection.gallery}}" stepKey="seeProductGallery" /> + <seeElement selector="{{StorefrontProductMediaSection.productImage(productImage)}}" stepKey="seeProductImage" /> + <click selector="{{StorefrontProductMediaSection.productImage(productImage)}}" stepKey="openFullscreenImage" /> + <waitForPageLoad stepKey="waitForGalleryLoaded" /> + <seeElement selector="{{StorefrontProductMediaSection.productImageFullscreen(productImage)}}" stepKey="seeFullscreenProductImage" /> + <click selector="{{StorefrontProductMediaSection.closeFullscreenImage}}" stepKey="closeFullScreenImage" /> + <waitForPageLoad stepKey="waitForGalleryDisappear" /> +</actionGroup> +``` + +{:style="color:red"} +BAD: + +```xml +<actionGroup name="StorefrontAssertProductImagesOnProductPageActionGroup"> + <arguments> + <argument name="productImage" type="string" /> + </arguments> + <waitForElementNotVisible selector="{{StorefrontProductMediaSection.gallerySpinner}}" stepKey="waitGallerySpinnerDisappear" /> + <seeElement selector="{{StorefrontProductMediaSection.gallery}}" stepKey="seeProductGallery" /> + <seeElement selector="{{StorefrontProductMediaSection.productImage(productImage)}}" stepKey="seeProductImage" /> + <click selector="{{StorefrontProductMediaSection.productImage(productImage)}}" stepKey="openFullscreenImage" /> + <waitForPageLoad stepKey="waitForGalleryLoaded" /> + <seeElement selector="{{StorefrontProductMediaSection.productImageFullscreen(productImage)}}" stepKey="seeFullscreenProductImage" /> + <click selector="{{StorefrontProductMediaSection.closeFullscreenImage}}" stepKey="closeFullScreenImage" /> + <waitForPageLoad stepKey="waitForGalleryDisappear" /> +</actionGroup> +``` + +### Build tests from action groups + +Build your tests using action groups, even if an action group contains a single action. + +**Why?** For extension developers, this will make it easier to extend or customize tests. +Extending a single action group will update all tests that use this group. +This improves maintainability as multiple instances of a failure can be fixed with a single action group update. + +{:style="color:green"} +GOOD: + +```xml +<test name="NavigateClamberWatchEntityTest"> + <annotations> + <!--some annotations--> + </annotations> + + <actionGroup ref="StorefrontOpenProductPageActionGroup" stepKey="openProductPage"> + <argument name="productUrl" value="{{ClamberWatch.url_key}}" /> + </actionGroup> + <actionGroup ref="StorefrontAssertProductNameOnProductPageActionGroup" stepKey="assertProductName"> + <argument name="productName" value="{{ClamberWatch.name}}" /> + </actionGroup> + <actionGroup ref="StorefrontAssertProductSkuOnProductPageActionGroup" stepKey="assertProductSku"> + <argument name="productSku" value="{{ClamberWatch.sku}}" /> + </actionGroup> + <actionGroup ref="StorefrontAssertProductPriceOnProductPageActionGroup" stepKey="assertProductPrice"> + <argument name="productPrice" value="{{ClamberWatch.price}}" /> + </actionGroup> + <actionGroup ref="StorefrontAssertProductImagesOnProductPageActionGroup" stepKey="assertProductImage"> + <argument name="productImage" value="{{ClamberWatch.image}}" /> + </actionGroup> +</test> +``` + +{:style="color:red"} +BAD: + +```xml +<test name="NavigateClamberWatchEntityTest"> + <annotations> + <!--some annotations--> + </annotations> + + <amOnPage url="{{StorefrontProductPage.url(ClamberWatch.url_key)}}" stepKey="openProductPage"/> + <see selector="{{StorefrontProductInfoMainSection.productName}}" userInput="{{ClamberWatch.name}}" stepKey="seeProductName" /> + <see selector="{{StorefrontProductInfoMainSection.productSku}}" userInput="{{ClamberWatch.sku}}" stepKey="seeProductSku" /> + <see selector="{{StorefrontProductInfoMainSection.price}}" userInput="{{ClamberWatch.price}}" stepKey="seeProductPrice" /> + <waitForElementNotVisible selector="{{StorefrontProductMediaSection.gallerySpinner}}" stepKey="waitGallerySpinnerDisappear" /> + <seeElement selector="{{StorefrontProductMediaSection.gallery}}" stepKey="seeProductGallery" /> + <seeElement selector="{{StorefrontProductMediaSection.productImage(ClamberWatch.productImage)}}" stepKey="seeProductImage" /> + <click selector="{{StorefrontProductMediaSection.productImage(ClamberWatch.productImage)}}" stepKey="openFullscreenImage" /> + <waitForPageLoad stepKey="waitForGalleryLoaded" /> + <seeElement selector="{{StorefrontProductMediaSection.productImageFullscreen(ClamberWatch.productImage)}}" stepKey="seeFullscreenProductImage" /> +</test> +``` + +### Use descriptive stepKey names + +Make `stepKeys` values as descriptive as possible. +Do not use numbers to make a `stepKey` unique. + +**Why?** This helps with readability and clarity. + +{:style="color:green"} +GOOD: + +```xml +<click selector="{{StorefrontNavigationSection.topCategory(SimpleSubCategory.name)}}" stepKey="clickSimpleSubCategoryLink" /> +<waitForPageLoad stepKey="waitForSimpleSubCategoryPageLoad" /> +<click selector="{{StorefrontCategoryMainSection.productLinkByHref(SimpleProduct.urlKey)}}" stepKey="clickSimpleProductLink" /> +<waitForPageLoad stepKey="waitForSimpleProductPageLoad" /> + +<!-- Perform some actions / Assert product page --> + +<click selector="{{StorefrontNavigationSection.topCategory(CustomCategory.name)}}" stepKey="clickCustomCategoryLink" /> +<waitForPageLoad stepKey="waitForCustomCategoryPageLoad" /> +<click selector="{{StorefrontCategoryMainSection.productLinkByHref(CustomSimpleProduct.urlKey)}}" stepKey="clickCustomSimpleProductLink" /> +<waitForPageLoad stepKey="waitForCustomSimpleProductPageLoad" /> +``` + +{:style="color:red"} +BAD: + +```xml +<click selector="{{StorefrontNavigationSection.topCategory(SimpleSubCategory.name)}}" stepKey="clickCategoryLink1" /> +<waitForPageLoad stepKey="waitForPageLoad1" /> +<click selector="{{StorefrontCategoryMainSection.productLinkByHref(SimpleProduct.urlKey)}}" stepKey="clickProductLink1" /> +<waitForPageLoad stepKey="waitForPageLoad2" /> + +<!-- Perform some actions / Assert product page --> + +<click selector="{{StorefrontNavigationSection.topCategory(CustomCategory.name)}}" stepKey="clickCategoryLink2" /> +<waitForPageLoad stepKey="waitForPageLoad3" /> +<click selector="{{StorefrontCategoryMainSection.productLinkByHref(CustomSimpleProduct.urlKey)}}" stepKey="clickProductLink2" /> +<waitForPageLoad stepKey="waitForPageLoad4" /> +``` + +**Exception:** + +Use numbers within `stepKeys` when order is important, such as with testing sort order. + +```xml +<createData entity="BasicMsiStock1" stepKey="createCustomStock1"/> +<createData entity="BasicMsiStock2" stepKey="createCustomStock2"/> +<createData entity="BasicMsiStock3" stepKey="createCustomStock3"/> +<createData entity="BasicMsiStock4" stepKey="createCustomStock4"/> +``` + +## Selectors + +### Build selectors in proper order + +When building selectors for form elements, start with the parent context of the form element. +Then specify the element `name` attribute in your selector to ensure the correct element is targeted. +To build a selector for an input, use the pattern: `{{section_selector}} {{input_selector}}` or for a button: `{{section_selector}} {{button_selector}}` + +**Why?** Traversing the DOM takes a finite amount of time and reducing the scope of the selector makes the selector lookup as efficient as possible. + +Example: + +```xml +<div class="admin__field _required" data-bind="css: $data.additionalClasses, attr: {'data-index': index}, visible: visible" data-index="name"> + <div class="admin__field-label" data-bind="visible: $data.labelVisible"> + <span data-bind="attr: {'data-config-scope': $data.scopeLabel}, i18n: label" data-config-scope="[STORE VIEW]">Product Name</span> + </div> + <div class="admin__field-control" data-bind="css: {'_with-tooltip': $data.tooltip, '_with-reset': $data.showFallbackReset && $data.isDifferedFromDefault}"> + <input class="admin__control-text" type="text" name="product[name]" aria-describedby="notice-EXNI71H" id="EXNI71H" maxlength="255" data-bind=" + attr: { + name: inputName, + placeholder: placeholder, + maxlength: 255}"/> + </div> +</div> +``` + +{:style="color:green"} +GOOD: + +```xml +<element name="productName" type="input" selector="*[data-index='product-details'] input[name='product[name]']"/> +``` + +{:style="color:red"} +BAD: + +```xml +<element name="productName" type="input" selector=".admin__field[data-index=name] input"/> +``` + +### Build selectors with appropriate specificity + +Selectors that are too general might sweep up unexpected elements. +When possible, select the first parent tag and then specify the desired element within that selection. + +**Why?** Elements that are overly specific are less flexible and may fail if unexpected DOM changes occur. It also reduces the amount of the DOM it needs to parse. + + {:style="color:green"} +GOOD: + +```html + form[name='myform'] > input[name='firstname'] + + //*[@id='container'][@class='dashboard-title'] + ``` + + {:style="color:red"} +BAD: + +```html + input[name='firstname'] + + //*[@id='container']/*[@class='dashboard-advanced-reports']/*[@class='dashboard-advanced- reports-description']/*[@class='dashboard-title'] + ``` + +## General tips + +### Use descriptive variable names + +Use descriptive variable names to increase readability. +**Why?** It makes the code easier to follow and update. + + {:style="color:green"} +GOOD: + +```xml +<element name="storeName" type="checkbox" selector="//label[contains(text(),'{{storeName}}')]" parameterized="true"/> +``` + +{:style="color:red"} +BAD: + +```xml +<element name="storeName" type="checkbox" selector="//label[contains(text(),'{{var1}}')]" parameterized="true"/> +``` + +### Use proper checkbox actions + +When working with input type `checkbox`, do not use the `click` action; use `checkOption` or `uncheckOption` instead. +**Why?** A click does not make it clear what the ending state will be; it will simply toggle the current state. Using the proper actions will ensure the expected state of the checkbox. + +{:style="color:green"} +GOOD: + +```xml +<checkOption selector="{{ProductInWebsitesSection.website('Second Website')}}" stepKey="selectSecondWebsite"/> +<uncheckOption selector="{{ProductInWebsitesSection.website('Second Website')}}" stepKey="unselectSecondWebsite"/> +``` + +{:style="color:red"} +BAD: + +```xml +<click selector="{{ProductInWebsitesSection.website('Second Website')}}" stepKey="selectSecondWebsite"/> +<click selector="{{ProductInWebsitesSection.website('Second Website')}}" stepKey="unselectSecondWebsite"/> +``` + +<!-- {% endraw %} --> diff --git a/docs/troubleshooting.md b/docs/troubleshooting.md new file mode 100644 index 000000000..9c90573e0 --- /dev/null +++ b/docs/troubleshooting.md @@ -0,0 +1,64 @@ +--- +mftf-release: 2.0.2 +redirect_from: /guides/v2.3/magento-functional-testing-framework/2.3/troubleshooting.html +--- + +# Troubleshooting + +_This topic was updated due to the {{page.mftf-release}} MFTF release._ +{: style="text-align: right"} + +Having a little trouble with the MFTF? See some common errors and fixes below. + +## WebDriver issues + +Troubleshoot your WebDriver issues on various browsers. + +### PhantomJS + +You are unable to upload file input using the MFTF actions and are seeing the following exception: + +```terminal +[Facebook\WebDriver\Exception\NoSuchDriverException] +No active session with ID e56f9260-b366-11e7-966b-db3e6f35d8e1 +``` + +#### Reason + +Use of PhantomJS is not actually supported by the MFTF. + +#### Solution + +For headless browsing, the [Headless Chrome](https://developers.google.com/web/updates/2017/04/headless-chrome){:target="_blank"} has better compatibility with the MFTF. + +### Chrome + +You are seeing an "unhandled inspector error" exception: + +```terminal +[Facebook\WebDriver\Exception\UnknownServerException] +unknown error: undhandled inspector error: {"code":-32601, "message": +"'Network.deleteCookie' wasn't found"} .... +``` + +![Screenshot with the exception](./img/trouble-chrome232.png) + +#### Reason + +Chrome v62 is in the process of being rolled out, and it causes an error with ChromeDriver v2.32+. + +#### Solution + +Use [ChromeDriver v2.33+](https://chromedriver.storage.googleapis.com/index.html?path=2.33/){:target="_blank"} and [Selenium Server Standalone v3.6.0+](http://www.seleniumhq.org/download/){:target="_blank"} in order to execute tests in Google Chrome v62+. + +### Firefox + +Tests that use the `moveMouseOver` action cause an error when run locally. + +#### Reason + +There's a compatibility issue with Codeception's `moveMouseOver` function and GeckoDriver with Firefox. + +#### Solution + +None yet. Solving this problem is dependent on a GeckoDriver fix. diff --git a/docs/update.md b/docs/update.md new file mode 100644 index 000000000..edaaefea3 --- /dev/null +++ b/docs/update.md @@ -0,0 +1,82 @@ +--- +mftf-release: 2.3.0 +redirect_from: /guides/v2.3/magento-functional-testing-framework/2.3/update.html +--- + +# Update the Magento Functional Testing Framework + +_This topic was updated due to the {{page.mftf-release}} MFTF release._ +{: style="text-align: right"} + +{% include_relative include/note-2.2-docs.md %} + +Magento tests and the framework are stored in different repositories. + +Magento tests are stored in the same repository as the Magento code base. +When you pull changes in the Magento code, you're potentially pulling corresponding tests as well. + +The MFTF is installed separately as a dependency using Composer. +When pulling the latest Magento code, update the corresponding Composer dependencies in the `magento2` root directory. +This ensures that the MFTF is up to date. + +## Update the MFTF from 2.3.x + +To update the MFTF to the latest patch: + +1. Verify that the Magento [WYSIWYG settings][] and [Security settings][] are set appropriately. +1. Check details about backward incompatible changes in the [Changelog][] and update your new or customized tests. +1. Get the latest framework version using Composer: + + ```bash + composer update + ``` + +1. Generate the updated tests: + + ```bash + vendor/bin/mftf generate:tests + ``` + +## Update the MFTF from 2.2 + +To update the MFTF from the previous minor version: + +1. When you update Magento, verify that the Magento [WYSIWYG settings][] and [Security settings][] are set appropriately. +1. Starting at the `magento2/` root directory remove the `vendor/` directory: + + ```bash + rm -rf vendor/ + ``` + +1. Get the latest framework version from the Composer dependencies: + + ```bash + composer install + ``` + +1. Run the `upgrade:tests` using the new command line tool: + + ```bash + vendor/bin/mftf upgrade:tests app + ``` + +1. If you are using Phpstorm, update the urn catalog: + + ```bash + vendor/bin/mftf generate:urn-catalog .idea/ + ``` + +1. Update your own tests, including data, metadata, and so on, if they contain tags that are unsupported in the newer version. + + Check details about backward incompatible changes and review new MFTF release documentation in the [Changelog][]. + +1. Generate newly pulled tests: + + ```bash + vendor/bin/mftf generate:tests + ``` + +<!-- Link Definitions --> +[Changelog]: https://github.com/magento/magento2-functional-testing-framework/blob/master/CHANGELOG.md +[WYSIWYG settings]: getting-started.html#wysiwyg-settings +[Security settings]: getting-started.html#security-settings \ No newline at end of file diff --git a/docs/versioning.md b/docs/versioning.md new file mode 100644 index 000000000..f74b47b2f --- /dev/null +++ b/docs/versioning.md @@ -0,0 +1,57 @@ +--- +title: Versioning Policy for MFTF +--- + +This documemt describes the versioning policy for the Magento Functional Testing Framework (MFTF), including the version numbering schema. + +## 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. + +## Find your MFTF version number + +To find the version of MFTF that you are using, run the Magento CLI command: + +```bash +cd <magento_root>/ +vendor/bin/mftf --version +``` + +## Versioning Policy + +MFTF versioning policy follows [Semantic Versioning](https://semver.org/) guidelines. + +### 3-component version numbers + +Version numbering schemes help users to understand the scope of the changes made a new release. + +X.Y.Z +| | | +| | +-- Backward Compatible changes (Patch release - bug fixes, small additions) +| +---- Backward Compatible changes (Minor release - small new features, bug fixes) ++------ Backward Incompatible changes (Major release - new features and/or major changes) + +For example: + +- Magento 2 ships with MFTF version 2.3.9 +- A patch is added to fix a bug: 2.3.10 (Increment Z = backward compatible change) +- New action command added: 2.4.0 (Increment Y, set Z to 0 = backward compatible change) +- New action added: 2.4.1 (Increment Z = backward compatible change) +- Major new features added to MFTF to support changes in Magento codebase: 3.0.0. (Increment X, reset Y and Z to 0 = backward incompatible change) + +### Z release - patch + +Patch version **Z** MUST be incremented for a release that introduces only backward compatible changes. + +### Y release - minor + +Minor version **Y** MUST be incremented for a release that introduces new, backward compatible features. +It MUST be incremented if any test or test entity is marked as deprecated. +It MAY include patch level changes. Patch version MUST be reset to 0 when minor version is incremented. + +### X release - major + +Major version **X** MUST be incremented for a release that introduces backward incompatible changes. +A major release can also include minor and patch level changes. +You must reset the patch and minor version to 0 when you change the major version. From 7f0207acfe3078920c3ce4d65753b077c5ef38b0 Mon Sep 17 00:00:00 2001 From: Donald Booth <dobooth@adobe.com> Date: Wed, 6 Mar 2019 16:01:25 -0600 Subject: [PATCH 02/18] Change a lot of code as we move MFTF docs to new repo. --- docs/best-practices.md | 14 +- docs/commands/codeception.md | 21 +- docs/commands/mftf.md | 23 +-- docs/configuration.md | 18 +- docs/credentials.md | 18 +- docs/data.md | 29 +-- docs/debugging.md | 5 +- docs/extending.md | 16 +- docs/getting-started.md | 50 +++-- docs/{test => }/img/action-groups-dia.svg | 0 docs/include/note-2.2-docs.md | 19 -- docs/introduction.md | 20 +- docs/merging.md | 22 +-- docs/metadata.md | 52 ++--- docs/page.md | 29 +-- docs/reporting.md | 21 +- docs/section.md | 34 ++-- docs/section/locator-functions.md | 19 +- docs/section/parameterized-selectors.md | 19 +- docs/suite.md | 29 +-- docs/test.md | 42 ++-- docs/test/action-groups.md | 30 ++- docs/test/actions.md | 222 +++++++++++----------- docs/test/annotations.md | 15 +- docs/test/assertions.md | 97 +++++----- docs/tips-tricks.md | 72 ++++--- docs/troubleshooting.md | 8 +- docs/update.md | 19 +- docs/versioning.md | 4 +- 29 files changed, 388 insertions(+), 579 deletions(-) rename docs/{test => }/img/action-groups-dia.svg (100%) delete mode 100644 docs/include/note-2.2-docs.md diff --git a/docs/best-practices.md b/docs/best-practices.md index 013c5e7be..3a03921ac 100644 --- a/docs/best-practices.md +++ b/docs/best-practices.md @@ -1,12 +1,6 @@ ---- -mftf-release: 2.3.0 -redirect_from: /guides/v2.3/magento-functional-testing-framework/2.3/best-practices.html ---- - # Best practices -_This topic was updated due to the {{ page.mftf-release }} MFTF release._ -{: style="text-align: right"} +<span style="text-align: right">_This topic was updated due to the 2.3.13 MFTF release._</span> Check out our best practices below to ensure you are getting the absolute most out of the Magento Functional Testing Framework. @@ -111,15 +105,17 @@ Use a lower case first letter for: Use [parameterized selectors] for constructing a selector when test specific or runtime generated information is needed. Do not use them for static elements. -{:style="color:red"} +<span class="color:red"> BAD: +</span> ``` xml <element name="relatedProductSectionText" type="text" selector=".fieldset-wrapper.admin__fieldset-section[data-index='{{productType}}']" parameterized="true"/> ``` -{:style="color:green"} +<span class="color:green"> GOOD: +</span> Define these three elements and reference them by name in the tests. diff --git a/docs/commands/codeception.md b/docs/commands/codeception.md index 7d0ac7b0d..e9694523f 100644 --- a/docs/commands/codeception.md +++ b/docs/commands/codeception.md @@ -1,16 +1,10 @@ ---- -mftf-release: 2.3.0 -redirect_from: /guides/v2.3/magento-functional-testing-framework/2.3/commands/codeception.html ---- - # CLI commands: vendor/bin/codecept -_This topic was updated due to the {{page.mftf-release}} MFTF release._ -{: style="text-align: right"} +<span style="text-align: right">_This topic was updated due to the 2.3.13 MFTF release._</span> -{:.bs-callout .bs-callout-warning} +<span class="bs-callout bs-callout-warning"> We do not recommend using Codeception commands directly as they can break the MFTF basic workflow. -All the Codeception commands you need are wrapped using the [`mftf` tool][]. +All the Codeception commands you need are wrapped using the [`mftf` tool][].</span> To run the Codeception testing framework commands directly, change your directory to the `<Magento root>`. @@ -42,8 +36,9 @@ vendor/bin/codecept run functional --group example --skip-group skip vendor/bin/codecept run ``` -{: .bs-callout .bs-callout-info } +<span class="bs-callout .bs-callout-info" The following documentation corresponds to Codeception 2.3.8. +</span> ```bash Full reference: @@ -70,7 +65,7 @@ Options: --coverage-xml Generate CodeCoverage XML report in file (default: "coverage.xml") --coverage-text Generate CodeCoverage text report in file (default: "coverage.txt") --coverage-phpunit Generate CodeCoverage PHPUnit report in file (default: "coverage-phpunit") - --no-exit Don't finish with exit code + --no-exit Do not finish with exit code --group (-g) Groups of tests to be executed (multiple values allowed) --skip (-s) Skip selected suites (multiple values allowed) --skip-group (-x) Skip selected groups (multiple values allowed) @@ -87,5 +82,5 @@ Options: <!-- Link definitions --> -[`mftf` tool]: mftf.html -[annotation]: ../test/annotations.html \ No newline at end of file +[`mftf` tool]: mftf.md +[annotation]: ../test/annotations.md \ No newline at end of file diff --git a/docs/commands/mftf.md b/docs/commands/mftf.md index b5617d4b3..7bdbf2f29 100644 --- a/docs/commands/mftf.md +++ b/docs/commands/mftf.md @@ -1,20 +1,10 @@ ---- -mftf-release: 2.3.11 -redirect_from: /guides/v2.3/magento-functional-testing-framework/2.3/commands/mftf.html ---- - # CLI commands: vendor/bin/mftf -_This topic was updated due to the {{page.mftf-release}} MFTF release._ -{: style="text-align: right"} +<span class="style="text-align: right"">_This topic was updated due to the 2.3.13 MFTF release._</span> The Magento Functional Testing Framework (MFTF) introduces the command line interface (CLI) tool `vendor/bin/mftf` to facilitate your interaction with the framework. -{% -include note.html -type='info' -content='`mftf` commands replace the `robo` commands that were used in previous releases.' -%} +Note that `mftf` commands replace the `robo` commands that were used in previous releases. ## Command format @@ -179,10 +169,7 @@ The command that encodes this complex configuration: vendor/bin/mftf generate:tests --tests "{\r\n\"tests\":[\r\n\"general_test1\",\r\n\"general_test2\",\r\n\"general_test3\"\r\n],\r\n\"suites\":{\r\n\"sample\":[\r\n\"suite_test1\"\r\n],\r\n\"sample2\":null\r\n}\r\n}" ``` -{% include note.html -type='info' -content='The strings must be escaped and surrounded in quotes.' -%} +Note: The strings must be escaped and surrounded in quotes. ### `generate:suite` @@ -399,11 +386,11 @@ vendor/bin/mftf upgrade:tests /Users/user/magento2/app/code/Magento/Catalog/Test <!-- LINK DEFINITIONS --> -[configuration]: ../configuration.html +[configuration]: ../configuration.md [Reference]: #reference [build]: #buildproject [setup]: #setupenv -[Reporting]: ../reporting.html +[Reporting]: ../reporting.md <!-- Abbreviations --> diff --git a/docs/configuration.md b/docs/configuration.md index f54498356..dfd5f7134 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -1,12 +1,6 @@ ---- -mftf-release: 2.3.7 -redirect_from: /guides/v2.3/magento-functional-testing-framework/2.3/configuration.html ---- - # Configuration -_This topic was updated due to the {{page.mftf-release}} MFTF release._ -{: style="text-align: right"} +<span style="text-align: right">_This topic was updated due to the 2.3.13 MFTF release._</span> The `*.env` file provides additional configuration for the Magento Functional Testing Framework (MFTF). To run the MFTF on your Magento testing instance, specify the basic configuration values. @@ -26,8 +20,9 @@ Example: MAGENTO_BASE_URL=http://magento2.vagrant251 ``` -{: .bs-callout .bs-callout-info } +<span class=".bs-callout .bs-callout-info"> If the `MAGENTO_BASE_URL` contains a subdirectory (like `http://magento.test/magento2ce`), specify [`MAGENTO_CLI_COMMAND_PATH`][]. +</span> ### MAGENTO_BACKEND_NAME @@ -93,9 +88,10 @@ SELENIUM_PROTOCOL=http SELENIUM_PATH=/wd/hub ``` -{:.bs-callout .bs-callout-warning} +<span class=".bs-callout .bs-callout-warning"> `SELENIUM_*` values are required if you are running Selenium on an external system. If you change the configuration of the external Selenium server, you must update these values. +</span> #### SELENIUM_HOST @@ -260,6 +256,6 @@ BROWSER=firefox <!-- Link definitions --> [`MAGENTO_CLI_COMMAND_PATH`]: #magento_cli_command_path -[generateDate]: test/actions.html#generatedate -[mftf]: commands/mftf.html +[generateDate]: test/actions.md#generatedate +[mftf]: commands/mftf.md [timezones]: http://php.net/manual/en/timezones.php \ No newline at end of file diff --git a/docs/credentials.md b/docs/credentials.md index c5803c83b..70fac4da6 100644 --- a/docs/credentials.md +++ b/docs/credentials.md @@ -1,6 +1,3 @@ ---- ---- - # Credentials When you test functionality that involves external services such as UPS, FedEx, PayPal, or SignifyD, use the MFTF credentials feature to hide sensitive [data][] like integration tokens and API keys. @@ -69,7 +66,6 @@ carriers_usps_password=Lmgxvrq89uPwECeV ## Use credentials in a test -<!--{% raw %}--> Access the data defined in the `.credentials` file using the [`fillField`][] action with the `userInput` attribute. Define the value as a reference to the corresponding key in the credentials file such as `{{_CREDS.my_data_key}}`: @@ -89,13 +85,11 @@ The MFTF dynamically retrieves, encrypts, and decrypts the sensitive data during Decrypted credentials do not appear in the console, error logs, or [test reports][]. The decrypted values are only available in the `.credentials` file. -{: .bs-callout .bs-callout-info } -The MFTF tests delivered with Magento application do not use credentials and do not cover external services, because of sensitivity of the data. - -<!--{% endraw %}--> +<span class=".bs-callout .bs-callout-info"> +The MFTF tests delivered with Magento application do not use credentials and do not cover external services, because of sensitivity of the data.</span> <!-- Link definitions --> -[`fillField`]: test/actions.html#fillfield -[data]: data.html -[initial setup]: getting-started.html -[test reports]: reporting.html +[`fillField`]: test/actions.md#fillfield +[data]: data.md +[initial setup]: getting-started.md +[test reports]: reporting.md diff --git a/docs/data.md b/docs/data.md index c3c588275..384a39105 100644 --- a/docs/data.md +++ b/docs/data.md @@ -1,21 +1,14 @@ ---- -mftf-release: 2.3.0 -redirect_from: /guides/v2.3/magento-functional-testing-framework/2.3/data.html ---- - # Input testing data -_This topic was updated due to the {{page.mftf-release}} MFTF release._ -{: style="text-align: right"} +<span style="text-align: right">_This topic was updated due to the 2.3.13 MFTF release._</span> The MFTF enables you to specify and use `<data>` entities defined in XML. Default `<data>` entities are provided for use and as templates for entity creation and manipulation. The following diagram shows the XML structure of an MFTF data object: -{%include_relative img/data-dia.svg%} +<img src="img/data-dia.svg" /> ## Supply data to test by reference to a data entity -{%raw%} Test steps requiring `<data>` input in an action, like filling a field with a string, may reference an attribute from a data entity: ```xml @@ -64,28 +57,22 @@ Example of referencing `data` in a test: userInput="$createCustomer.email$" ``` -{%endraw%} In this example: * `createCustomer` is a step key of the corresponding test step that creates an entity. * `email` is a data key of the entity. The corresponding value will be assigned to `userInput` as a result. -{% -include note.html -type="info" -content='As of MFTF 2.3.6, you no longer need to differentiate between scopes (a test, a hook, or a suite) for persisted data when referencing it in tests. +Note: As of MFTF 2.3.6, you no longer need to differentiate between scopes (a test, a hook, or a suite) for persisted data when referencing it in tests. The MFTF now stores the persisted data and attempts to retrieve it using the combination of `stepKey` and the scope of where it has been called. The current scope is preferred, then widening to _test > hook > suite_ or _hook > test > suite_. -This emphasizes the practice for the `stepKey` of `createData` to be descriptive and unique, as a duplicated `stepKey` in both a `<test>` and `<before>` prefers the `<test>` data.' -%} +This emphasizes the practice for the `stepKey` of `createData` to be descriptive and unique, as a duplicated `stepKey` in both a `<test>` and `<before>` prefers the `<test>` data. ## Use data returned by test actions -{%raw%} -A test can also reference data that was returned as a result of [test actions](./test/actions.html#actions-returning-a-variable), like the action `<grabValueFrom selector="someSelector" stepKey="grabStepKey>`. +A test can also reference data that was returned as a result of [test actions](./test/actions.md#actions-returning-a-variable), like the action `<grabValueFrom selector="someSelector" stepKey="grabStepKey>`. Further in the test, the data grabbed by the `someSelector` selector can be referenced using the `stepKey` value. In this case, it is `grabStepKey`. @@ -100,7 +87,7 @@ The following example shows the usage of `grabValueFrom` in testing, where the r The data to operate against can be included as literals in a test. Hard-coded data input can be useful in assertions. -See also [Actions](./test/actions.html). +See also [Actions](./test/actions.md). ```xml userInput="We'll email you an order confirmation with details and tracking info." @@ -261,6 +248,4 @@ Attributes|Type|Use|Description ### item {#item-tag} -`<item>` is an individual piece of data to be passed in as part of the parent `<array>` type. - -{%endraw%} +`<item>` is an individual piece of data to be passed in as part of the parent `<array>` type. \ No newline at end of file diff --git a/docs/debugging.md b/docs/debugging.md index f057756bf..f80ebaebb 100644 --- a/docs/debugging.md +++ b/docs/debugging.md @@ -1,7 +1,4 @@ ---- -title: Debugging MFTF Tests -mftf-release: 2.3.13 ---- +# Debugging Debugging within the Magento Functional Testing Framework is helpful in identifying test bugs by allowing you to pause execution so that you may: diff --git a/docs/extending.md b/docs/extending.md index 7bd36c31c..7395ece61 100644 --- a/docs/extending.md +++ b/docs/extending.md @@ -1,12 +1,6 @@ ---- -mftf-release: 2.3.0 -redirect_from: /guides/v2.3/magento-functional-testing-framework/2.3/extending.html ---- - # Extending -_This topic was updated due to the {{page.mftf-release}} MFTF release._ -{: style="text-align: right"} +<span style="text-align: right">_This topic was updated due to the 2.3.13 MFTF release._</span> There are cases when you need to create many tests that are very similar to each other. For example, only one or two parameters (for example, URL) might vary between tests. @@ -366,7 +360,7 @@ __Use case__: Create an entity named `DivPanelGreen`, which is similar to the `D ``` <!-- Link definitions --> -[test]: ./test.html -[data]: ./data.html -[action group]: ./test/action-groups.html -[actions]: ./test/actions.html \ No newline at end of file +[test]: ./test.md +[data]: ./data.md +[action group]: ./test/action-groups.md +[actions]: ./test/actions.md \ No newline at end of file diff --git a/docs/getting-started.md b/docs/getting-started.md index b49ab76d2..d1dfda2fb 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -1,14 +1,13 @@ ---- -mftf-release: 2.3.8 -redirect_from: /guides/v2.3/magento-functional-testing-framework/2.3/getting-started.html ---- - # Getting started -_This topic was updated after {{page.mftf-release}} MFTF release._ -{: style="text-align: right"} +<span style="text-align: right">_This topic was updated due to the 2.3.13 MFTF release._</span> + +<div class="bs-callout bs-callout-info"> +<a href="https://devdocs.magento.com/mftf/2.3/introduction.html#find-version">Find out your version</a> of the MFTF. -{% include_relative include/note-2.2-docs.md %} +The latest Magento 2.3 release supports MFTF 2.3.13. +The latest Magento 2.2 release supports MFTF 2.3.8. +</div> ## Prepare environment {#prepare-environment} @@ -62,23 +61,24 @@ Configure the following settings in Magento as described below. ### WYSIWYG settings {#wysiwyg-settings} -A Selenium web driver cannot enter data to fields with {% glossarytooltip 98cf4fd5-59b6-4610-9c1f-b84c8c0abd97 %}WYSIWYG{% endglossarytooltip %}. +A Selenium web driver cannot enter data to fields with WYSIWYG. To disable the WYSIWYG and enable the web driver to process these fields as simple text areas: -1. Log in to the {% glossarytooltip 18b930cf-09cc-47c9-a5e5-905f86c43f81 %}Magento Admin{% endglossarytooltip %} as an administrator. +1. Log in to the Magento Admin as an administrator. 2. Navigate to **Stores \> Configuration \> General \> Content Management**. 3. In the WYSIWYG Options section set the **Enable WYSIWYG Editor** option to **Disabled Completely**. 4. Click **Save Config**. -{: .bs-callout .bs-callout-tip } +<span class=".bs-callout .bs-callout-tip"> When you want to test the WYSIWYG functionality, re-enable WYSIWYG in your test suite. +</span> ### Security settings {#security-settings} To enable the **Admin Account Sharing** setting, to avoid unpredictable logout during a testing session, and disable the **Add Secret Key in URLs** setting, to open pages using direct URLs: -1. Navigate to **Stores \> Configuration \> Advanced \> {% glossarytooltip 29ddb393-ca22-4df9-a8d4-0024d75739b1 %}Admin{% endglossarytooltip %} \> Security**. +1. Navigate to **Stores \> Configuration \> Advanced \> Admin \> Security**. 2. Set **Admin Account Sharing** to **Yes**. 3. Set **Add Secret Key to URLs** to **No**. 4. Click **Save Config**. @@ -103,9 +103,7 @@ In the Magento project root, run: vendor/bin/mftf build:project ``` -{% include note.html -type='tip' -content='If you use PhpStorm, generate a URN catalog: +If you use PhpStorm, generate a URN catalog: ```bash vendor/bin/mftf generate:urn-catalog .idea/ @@ -118,11 +116,11 @@ vendor/bin/mftf generate:urn-catalog --force .idea/ ``` See [`generate:urn-catalog`][] for more details.' -%} -{:.bs-callout .bs-callout-tip} +<span class=".bs-callout .bs-callout-tip"> You can simplify command entry by adding the absolute path to the `vendor/bin` directory path to your PATH environment variable. After adding the path, you can run `mftf` without having to include `vendor/bin`. +</span> ### Step 2. Edit environmental settings {#environment-settings} @@ -288,24 +286,24 @@ allure serve dev/tests/_output/allure-results/ <!-- Link definitions --> -[`codecept`]: commands/codeception.html -[`generate:urn-catalog`]: commands/mftf.html#generateurn-catalog -[`MAGENTO_BP`]: configuration.html#magento_bp -[`mftf`]: commands/mftf.html +[`codecept`]: commands/codeception.md +[`generate:urn-catalog`]: commands/mftf.md#generateurn-catalog +[`MAGENTO_BP`]: configuration.md#magento_bp +[`mftf`]: commands/mftf.md [allure docs]: https://docs.qameta.io/allure/ [Allure Framework]: http://allure.qatools.ru/ -[basic configuration]: configuration.html#basic-configuration +[basic configuration]: configuration.md#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/ -[Configuration]: configuration.html +[Configuration]: configuration.md [contributing]: https://github.com/magento/magento2-functional-testing-framework/blob/develop/.github/CONTRIBUTING.md [install Allure]: https://github.com/allure-framework/allure2#download [java]: http://www.oracle.com/technetwork/java/javase/downloads/index.html -[mftf tests]: introduction.html#mftf-tests -[php]: {{ site.gdeurl23 }}install-gde/system-requirements-tech.html#php +[mftf tests]: introduction.md#mftf-tests +[php]: https://devdocs.magento.com/guides/2.3/install-gde/system-requirements-tech.md#php [PhpStorm]: https://www.jetbrains.com/phpstorm/ [selenium server]: https://www.seleniumhq.org/download/ [Set up a standalone MFTF]: #set-up-a-standalone-mftf -[test suite]: suite.html +[test suite]: suite.md diff --git a/docs/test/img/action-groups-dia.svg b/docs/img/action-groups-dia.svg similarity index 100% rename from docs/test/img/action-groups-dia.svg rename to docs/img/action-groups-dia.svg diff --git a/docs/include/note-2.2-docs.md b/docs/include/note-2.2-docs.md deleted file mode 100644 index ec41ef9ae..000000000 --- a/docs/include/note-2.2-docs.md +++ /dev/null @@ -1,19 +0,0 @@ -<div class="bs-callout bs-callout-info" markdown="1"> -[Find out your version]({{site.baseurl}}/mftf/2.3/introduction.html#find-version) of the MFTF. -If your version is 2.2, see the [MFTF 2.2 documentation]({{site.baseurl}}/mftf/2.2/introduction.html). - -{% assign packages_2_3 = site.data.codebase.v2_3.open-source.composer_lock.packages-dev %} -{% assign packages_2_2 = site.data.codebase.v2_2.open-source.composer_lock.packages-dev %} - -{% for package in packages_2_3 %} -{% if package.name contains 'magento2-functional-testing-framework' %} -The latest Magento 2.2 release supports MFTF {{ package.version }}. -{% endif %} -{% endfor %} - -{% for package in packages_2_2 %} -{% if package.name contains 'magento2-functional-testing-framework' %} -The latest Magento 2.3 release supports MFTF {{ package.version }}. -{% endif %} -{% endfor %} -</div> \ No newline at end of file diff --git a/docs/introduction.md b/docs/introduction.md index 524665a14..fbb86ad8c 100644 --- a/docs/introduction.md +++ b/docs/introduction.md @@ -1,14 +1,11 @@ ---- -mftf-release: 2.3.14 -redirect_from: /guides/v2.3/magento-functional-testing-framework/2.3/introduction.html ---- - # Introduction to the Magento Functional Testing Framework version 2.3 -_The latest MFTF release is [{{page.mftf-release}}]._ -{: style="text-align: right"} +<span style="text-align: right">_This topic was updated due to the 2.3.13 MFTF release._</span> + +<a href="https://devdocs.magento.com/mftf/2.3/introduction.html#find-version">Find out your version</a> of the MFTF. -{% include_relative include/note-2.2-docs.md %} +The latest Magento 2.3 release supports MFTF 2.3.13. +The latest Magento 2.2 release supports MFTF 2.3.8. The Magento Functional Testing Framework (MFTF) aims to replace the [Functional Testing Framework] in future releases. MFTF improves: @@ -20,10 +17,10 @@ MFTF improves: - **Maintainability** based on simple test creation and overall structure. Because MFTF tests are written in XML, you no longer need to learn PHP to write tests. - -{:.bs-callout .bs-callout-info} +<span class=".bs-callout .bs-callout-info"> We are actively developing functional tests. Refer to `<magento_root>/app/code/<vendor_name>/<module_name>/Test/Mftf/` for examples. +</span> ## Audience @@ -136,6 +133,5 @@ Follow the [MFTF project] and [contribute on Github]. <!-- Link definitions --> [contribute on Github]: https://github.com/magento/magento2-functional-testing-framework/blob/master/.github/CONTRIBUTING.md -[Functional Testing Framework]: {{ site.gdeurl23 }}mtf/mtf_introduction.html +[Functional Testing Framework]: https://devdocs.magento.com/guides/v2.3/mtf/mtf_introduction.html [MFTF project]: https://github.com/magento/magento2-functional-testing-framework -[{{page.mftf-release}}]: https://github.com/magento/magento2-functional-testing-framework/releases/tag/{{page.mftf-release}} diff --git a/docs/merging.md b/docs/merging.md index ac4488aeb..f4e9073a9 100644 --- a/docs/merging.md +++ b/docs/merging.md @@ -1,12 +1,6 @@ ---- -mftf-release: 2.3.0 -redirect_from: /guides/v2.3/magento-functional-testing-framework/2.3/merging.html ---- - # Merging -_This topic was updated due to the {{page.mftf-release}} MFTF release._ -{: style="text-align: right"} +<span style="text-align: right">_This topic was updated due to the 2.3.13 MFTF release._</span> The MFTF allows you to merge test components defined in XML files, such as: @@ -566,10 +560,10 @@ The `_defaultSample` results corresponds to: <!-- Link definitions --> -[`codecept`]: ./commands/codeception.html -[`mftf`]: ./commands/mftf.html -[`<data>`]: ./data.html -[elements]: ./section.html#element-tag -[`<pages>`]: ./page.html -[`<sections>`]: ./section.html -[`<tests>`]: ./test.html +[`codecept`]: ./commands/codeception.md +[`mftf`]: ./commands/mftf.md +[`<data>`]: ./data.md +[elements]: ./section.md#element-tag +[`<pages>`]: ./page.md +[`<sections>`]: ./section.md +[`<tests>`]: ./test.md diff --git a/docs/metadata.md b/docs/metadata.md index 6e507660f..cb87ffa68 100644 --- a/docs/metadata.md +++ b/docs/metadata.md @@ -1,12 +1,6 @@ ---- -mftf-release: 2.2.0 -redirect_from: /guides/v2.3/magento-functional-testing-framework/2.3/metadata.html ---- - # Metadata -_This topic was updated due to the {{page.mftf-release}} MFTF release._ -{: style="text-align: right"} +<span style="text-align: right">_This topic was updated due to the 2.3.13 MFTF release._</span> In this topic we talk about handling entities that you need in your tests (such as categories, products, wish lists, and similar) using the MFTF. Using data handling actions like [`createData`], [`deleteData`], [`updateData`], and [`getData`], you are able to create, delete, update, and read entities for your tests. @@ -16,7 +10,7 @@ The framework enables you to send HTTP requests with these statically defined da - [Handling a REST API response][rest response] - [Sending an HTML form encoded in URL][html form] -You have probably noticed that some modules in acceptance functional tests contain a directory, which is called `Metadata` . +You have probably noticed that some modules in acceptance functional tests contain a directory, which is called `Metadata`. Example of a module with _Metadata_: @@ -45,9 +39,9 @@ When a test step requires handling the specified data entity, the MFTF performs - Stores a response and provides access to its data using MFTF variables syntax in XML. The following diagram demonstrates the XML structure of a metadata file: -{% include_relative img/metadata-dia.svg %} +<img src="img/metadata-dia.svg" /> -## Format {#format} +## Format {#format} ```xml <?xml version="1.0" encoding="UTF-8"?> @@ -71,7 +65,7 @@ The following diagram demonstrates the XML structure of a metadata file: </operation> ``` -## Principles {#principles} +## Principles {#principles} 1. A `dataType` value must match the `type` value of the corresponding entity. 2. A file name should contain data type split with `_` and must end with `-meta`. @@ -104,15 +98,10 @@ Example: The MFTF allows you to handle basic CRUD operations with an object using [Magento REST API][api reference] requests. To convert a request to the MFTF format, wrap the corresponding REST API request into XML tags according to the [Reference documentation][reference]. -{% include note.html -type="info" -content="GET is used for retrieving data from objects. - - POST is used for creating new objects. - - PUT is used for updating objects. - - DELETE is used for deleting objects." %} +- GET is used for retrieving data from objects. +- POST is used for creating new objects. +- PUT is used for updating objects. +- DELETE is used for deleting objects. This is an example of how to handle a category using REST API operations provided by the `catalogCategoryRepositoryV1` service. @@ -194,13 +183,10 @@ The following is encoded in `<operation>`: The parameter that declares a body of the request is _catalogCategoryRepositoryV1SavePostBody_. Using the [Reference], we can trace how the JSON request was converted into XML representation. -{% include note.html -type="info" -content="Comments in the example below are used to demonstrate relation between JSON request and MFTF metadata in XML. -JSON does not support comments."%} +Note: Comments in the example below are used to demonstrate relation between JSON request and MFTF metadata in XML. +JSON does not support comments. Model schema for _catalogCategoryRepositoryV1SavePostBody_ with XML representation of _Catalog/Metadata/category-meta.xml_ in comments: -{:#catalogCategoryRepositoryV1SavePostBody} ```json { // XML representation in the MFTF metadata format (see 'Catalog/Metadata/category-meta.xml') @@ -568,30 +554,30 @@ Example: <!-- LINK DEFINITIONS --> -[actions]: test/actions.html -[api reference]: {{ site.gdeurl23 }}rest/bk-rest.html +[actions]: test/actions.md +[api reference]: https://devdocs.magento.com/guides/v2.3/get-started/bk-get-started-api.html [application/x-www-form-urlencoded]: https://www.w3.org/TR/html401/interact/forms.html#h-17.13.4.1 {:target="_blank"} [catalogCategoryRepositoryV1 image]: img/catalogCategoryRepository-operations.png [catalogCategoryRepositoryV1SavePostBody]: #catalogCategoryRepositoryV1SavePostBody [contentType]: #contentType-tag [Create a new category]: #create-object-as-adminOauth -[createData]: test/actions.html#createdata +[createData]: test/actions.md#createdata [delete a category by its ID]: #delete-object-as-adminOauth -[deleteData]: test/actions.html#deletedata -[entity]: data.html#entity-tag +[deleteData]: test/actions.md#deletedata +[entity]: data.md#entity-tag [get information about a category by its ID]: #get-object-as-adminOauth -[getData]: test/actions.html#getdata +[getData]: test/actions.md#getdata [HTML forms]: https://www.w3.org/TR/html401/interact/forms.html {:target="_blank"} -[oauth]: {{ site.gdeurl23 }}get-started/authentication/gs-authentication-oauth.html +[oauth]: https://devdocs.magento.com/guides/v2.3/get-started/authentication/gs-authentication-oauth.html {:target="_blank"} [operation]: #operation-tag [reference]: #reference [rest request]: #handling-with-api [html form]: #using-html-forms [update category data by its ID]: #update-object-as-adminOauth -[updateData]: test/actions.html#updatedata +[updateData]: test/actions.md#updatedata [rest response]: #rest-response *[CRUD]: Create Read Update Delete diff --git a/docs/page.md b/docs/page.md index f52913e40..04559917c 100644 --- a/docs/page.md +++ b/docs/page.md @@ -1,12 +1,6 @@ ---- -mftf-release: 2.2.0 -redirect_from: /guides/v2.3/magento-functional-testing-framework/2.3/page.html ---- - # Page structure -_This topic was updated due to the {{page.mftf-release}} MFTF release._ -{: style="text-align: right"} +<span style="text-align: right">_This topic was updated due to the 2.3.13 MFTF release._</span> The MFTF uses a modified concept of [PageObjects], which models the testing areas of your page as objects within the code. This reduces occurrences of duplicated code and enables you to fix things quickly, in one place, when things change. @@ -15,23 +9,17 @@ You define the contents of a page, for reference in a [`<test>`], at both the [` The `pageObject` lists the URL of the `page` and the `sections` that it contains. You can reuse `sections` to define the elements on a page that are exercisable, while also ensuring a single source of truth to help maintain consistency. -{% include note.html -type="tip" -content="Avoid hard-coded location selectors from tests to increase the maintainability and readability of the tests, as well as improve the test execution output and logging." -%} +Avoid hard-coded location selectors from tests to increase the maintainability and readability of the tests, as well as improve the test execution output and logging. Two types of pages are available: - {%raw%} - Page with `url` declared as a constant string or [explicit page] - In a test it is called in a format like `{{NameOfPage.url}}`, where `NameOfPage` is a value of `name` in the corresponding page declaration `*.xml` file. - Page with `url` declared as a string with one or more variables or [parameterized page] - In a test it is called using a format like `{{NameOfPage.url(var1, var2, ...)}}`, where `var1, var2` etc. are parameters that will be substituted in the `url` of the corresponding `<page>` declaration. -{%endraw%} - The following diagram shows the XML structure of an MFTF page: -{% include_relative img/page-dia.svg %} +<img src="img/page-dia.svg" /> ## Format @@ -95,7 +83,6 @@ The `AdminCategoryPage` declares four [sections][section]: - `AdminCategorySEOSection` - located in the `Catalog/Section/AdminCategorySEOSection.xml` file The following is an example of a call in test: -{%raw%} ```xml <amOnPage url="{{AdminCategoryPage.url}}" stepKey="navigateToAdminCategory"/> @@ -171,15 +158,13 @@ Attributes|Type|Use|Description `name`|string|required|Unique section name identifier. `remove`|boolean|optional|The default value is `"false"`. Set to `"true"` to remove this element during parsing. -{%endraw%} - <!-- Link definitions --> -[`<createData>`]: test/actions.html#createdata +[`<createData>`]: test/actions.md#createdata [`<page>`]: #page-tag [`<section>`]: #section-tag -[`<test>`]: test.html -[actions]: test/actions.html +[`<test>`]: test.md +[actions]: test/actions.md [explicit page]: #explicit-page [PageObjects]: https://github.com/SeleniumHQ/selenium/wiki/PageObjects [parameterized page]: #parameterized-page -[section]: section.html +[section]: section.md diff --git a/docs/reporting.md b/docs/reporting.md index a71c591a6..a6a69e45e 100644 --- a/docs/reporting.md +++ b/docs/reporting.md @@ -1,6 +1,3 @@ ---- ---- - # Reporting The Magento Functional Testing Framework provides two types of reporting: @@ -280,8 +277,9 @@ Each time you run tests, the MFTF appends an XML file with results at the `tests The official [Allure Test Report][] documentation is well-covered, so we'll list only the CLI commands that you would need for your day-to-day work. -{: .bs-callout .bs-callout-info } +<span class=".bs-callout .bs-callout-info"> The following commands are relative to the Magento installation directory. +</span> To generate a report to the `allure-report/` at the current directory: @@ -301,10 +299,7 @@ To launch the generated report in a web browser: allure open dev/tests/acceptance/tests/_output/allure-report ``` -{% include note.html -type='info' -content=' -By default, Allure generates reports in the `allure-report/` at the current directory. +Note: By default, Allure generates reports in the `allure-report/` at the current directory. For example, if you run the command without `-o` flag while you are in the `magento2/` directory, Allure will generate a report at the `magento2/allure-report/` directory. ```bash @@ -347,13 +342,13 @@ Refer to the [Reporting section][] for more Allure CLI details. <!-- Link definitions --> -[`after`]: test.html#after-tag -[`run:group`]: commands/mftf.html#rungroup -[`run:test`]: commands/mftf.html#runtest +[`after`]: test.md#after-tag +[`run:group`]: commands/mftf.md#rungroup +[`run:test`]: commands/mftf.md#runtest [Allure Framework]: https://docs.qameta.io/allure/ [Allure Test Report]: http://allure.qatools.ru/ -[codecept]: commands/codeception.html +[codecept]: commands/codeception.md [codeception]: https://codeception.com/docs/reference/Commands -[mftf]: commands/mftf.html +[mftf]: commands/mftf.md [report an issue]: https://github.com/magento/magento2-functional-testing-framework/blob/master/.github/CONTRIBUTING.md#report-an-issue [Reporting section]: https://docs.qameta.io/allure/#_reporting diff --git a/docs/section.md b/docs/section.md index a8c716cb1..804cf6d57 100644 --- a/docs/section.md +++ b/docs/section.md @@ -1,36 +1,27 @@ ---- -mftf-release: 2.1.2 -redirect_from: /guides/v2.3/magento-functional-testing-framework/2.3/section.html ---- - # Section structure -_This topic was updated due to the {{page.mftf-release}} MFTF release._ -{: style="text-align: right"} +<span style="text-align: right">_This topic was updated due to the 2.3.13 MFTF release._</span> -{%raw%} -A `<section>` is a reusable part of a [`<page>`](./page.html) and is the standard file for defining UI elements on a page used in a test. +A `<section>` is a reusable part of a [`<page>`](./page.md) and is the standard file for defining UI elements on a page used in a test. A `<section>` can define: - An explicit element that has a selector equal to the constant string. Example: `selector="#add_root_category_button"` - A parameterized element that contains substitutable values in the selector. Example: `selector="#element .{{var1}} .{{var2}}"`. -{% endraw %} - ## Substitutable values Substitutable values in the test can be of the following formats: - String literals (`stringLiteral`) -- References to a [data entity](./data.html) (XML data from the corresponding `.../Data/*.xml`) such as `entityName.Field`. +- References to a [data entity](./data.md) (XML data from the corresponding `.../Data/*.xml`) such as `entityName.Field`. - Persisted data: - - `$persistedCreateDataKey.field$` for data created in the scope of a [test](./test.html#test-tag) using the [`<createData>`](./test/actions.html#createdata) action with `stepKey="persistedCreateDataKey"`. - - `$$persistedCreateDataKey.field$$` for data created in [before](./test.html#before-tag) and [after](./test.html#after-tag) hooks. Even though `<before>`and `<after>` are nested inside a [test](./test.html#test-tag), persisted data is stored differently when it is done in a test hook. Therefore it must be accessed with a different notation. + - `$persistedCreateDataKey.field$` for data created in the scope of a [test](./test.md#test-tag) using the [`<createData>`](./test/actions.md#createdata) action with `stepKey="persistedCreateDataKey"`. + - `$$persistedCreateDataKey.field$$` for data created in [before](./test.md#before-tag) and [after](./test.md#after-tag) hooks. Even though `<before>`and `<after>` are nested inside a [test](./test.md#test-tag), persisted data is stored differently when it is done in a test hook. Therefore it must be accessed with a different notation. The following diagram shows the XML structure of an MFTF section: -{%include_relative img/section-dia.svg%} +<img src="img/section-dia.svg%" /> ## Format @@ -83,7 +74,6 @@ The `AdminCategorySidebarActionSection` section declares two buttons: - `addSubcategoryButton` - button with a `#add_subcategory_button` locator on the parent web page The following is an example of a call in test: -{%raw%} ```xml <!-- Click on the button with locator "#add_subcategory_button" on the web page--> @@ -94,7 +84,7 @@ The following is an example of a call in test: ### section {#section-tag} -`<section>` contains the sequence of UI elements in a section of a [page](./page.html). +`<section>` contains the sequence of UI elements in a section of a [page](./page.md). Attributes|Type|Use|Description ---|---|---|--- @@ -103,16 +93,16 @@ Attributes|Type|Use|Description ### element {#element-tag} -`<element>`is a UI element used in an [action](./test/actions.html). +`<element>`is a UI element used in an [action](./test/actions.md). Attributes|Type|Use|Description ---|---|---|--- `name`|string|required|The element name; Must be alphanumeric. `type`|string|required|The type of the element. Possible values: `text`, `textarea`, `input`, `button`, `checkbox`, `radio`, `checkboxset`, `radioset`, `date`, `file`, `select`, `multiselect`, `wysiwyg`, `iframe`, `block`. `selector`|string|optional|[XPath](https://www.w3schools.com/xml/xpath_nodes.asp) or [CSS](https://www.w3schools.com/cssref/css_selectors.asp) selector of the element. -`locatorFunction`|string|optional|[Locator function](./section/locator-functions.html) declaration to be used in lieu of a selector. +`locatorFunction`|string|optional|[Locator function](./section/locator-functions.md) declaration to be used in lieu of a selector. `timeout`|string|optional|The timeout after interaction with the element (in seconds). The default is _none_. -`parameterized`|boolean|optional|Include and set to `true` if the `selector` for this element has parameters that need to be replaced for proper use. Learn more in [Parameterized selectors](./section/parameterized-selectors.html). +`parameterized`|boolean|optional|Include and set to `true` if the `selector` for this element has parameters that need to be replaced for proper use. Learn more in [Parameterized selectors](./section/parameterized-selectors.md). `remove`|boolean|optional|The default is `false`. Set to `true` to remove this element during parsing. #### `timeout` attribute {#timeout-attribute} @@ -144,8 +134,6 @@ The test step that covers the use case: Whenever the `signIn` button is used in a test, the MFTF will add a 30 second `waitForPageLoad` action immediately after the `click`. -{% endraw %} - <!-- Link definitions --> -[waitForPageLoad]: test/actions.html#waitforpageload \ No newline at end of file +[waitForPageLoad]: test/actions.md#waitforpageload \ No newline at end of file diff --git a/docs/section/locator-functions.md b/docs/section/locator-functions.md index 9962c6f24..78df44f55 100644 --- a/docs/section/locator-functions.md +++ b/docs/section/locator-functions.md @@ -1,18 +1,10 @@ ---- -mftf-release: 2.0.2 -redirect_from: /guides/v2.3/magento-functional-testing-framework/2.3/section/locator-functions.html ---- - # Locator functions -_This topic was updated due to the {{page.mftf-release}} MFTF release._ -{: style="text-align: right"} - -{%raw%} +<span style="text-align: right">_This topic was updated due to the 2.3.13 MFTF release._</span> ## Define Locator::functions in elements - Codeception has a set of very useful [Locator functions](http://codeception.com/docs/reference/Locator) that may be used by elements inside a [section](../section.html). + Codeception has a set of very useful [Locator functions][] that may be used by elements inside a [section][]. Declare an element with a `locatorFunction`: @@ -26,7 +18,7 @@ When using the `locatorFunction`, omit `Locator::` for code simplicity: <element name="simpleLocatorShorthand" type="button" locatorFunction="contains('label', 'Name')"/> ``` -An element's `locatorFunction` can also be parameterized the same way as [parameterized selectors](./parameterized-selectors.html): +An element's `locatorFunction` can also be parameterized the same way as [parameterized selectors][]: ```xml <element name="simpleLocatorTwoParam" type="button" locatorFunction="contains({{arg1}}, {{arg2}})" parameterized="true"/> @@ -45,4 +37,7 @@ Given the above element definitions, you call the elements in a test just like a </test> ``` -{%endraw%} \ No newline at end of file +<!-- Link Definitions --> +[Locator functions]: http://codeception.com/docs/reference/Locator +[section]: ../section.md +[parameterized selectors]: ./parameterized-selectors.md \ No newline at end of file diff --git a/docs/section/parameterized-selectors.md b/docs/section/parameterized-selectors.md index 4051afdac..b9a9530eb 100644 --- a/docs/section/parameterized-selectors.md +++ b/docs/section/parameterized-selectors.md @@ -1,14 +1,7 @@ ---- -mftf-release: 2.0.2 -redirect_from: /guides/v2.3/magento-functional-testing-framework/2.3/section/parameterized-selectors.html ---- - # Parameterized selectors -_This topic was updated due to the {{page.mftf-release}} MFTF release._ -{: style="text-align: right"} +<span style="text-align: right">_This topic was updated due to the 2.3.13 MFTF release._</span> -{%raw%} Use the following examples to create and use parameterized selectors in the MFTF. ## Set up a selector in section @@ -79,12 +72,13 @@ For the parameterized part of the selector, add `{{var1}}, {{var2}}, ..., {{varN </section> ``` -{: .bs-callout .bs-callout-info } +<span class="bs-callout .bs-callout-info"> There is no need to use sequential variables like `{{var1}}`, `{{var2}}`. Parameterized replacement reads variables and maps them to the test call of the element sequentially from left to right, meaning you can use a selector like `#element .{{categoryId}} .{{productId}}`." +</span> ## Use a parameterized selector in a test -Create a new [test](../test.html): +Create a new [test][]: ```xml <test name="SampleTest"> @@ -155,6 +149,5 @@ Any data can be used in parameterized elements, as well as entered in test actio * `$createDataKey.id$` is a reference to persisted data created in the `SampleTest1` within the `stepKey="createDataKey"` action. * `{$variable}` is a reference to data returned by a test action, like `<grabValueFrom>`. -{%endraw%} - - +<!-- Link Definitions --> +[test]: ../test.md \ No newline at end of file diff --git a/docs/suite.md b/docs/suite.md index 543bb05ac..768856901 100644 --- a/docs/suite.md +++ b/docs/suite.md @@ -1,12 +1,6 @@ ---- -mftf-release: 2.3.9 -redirect_from: /guides/v2.3/magento-functional-testing-framework/2.3/suite.html ---- - # Suites -_This topic was updated due to the {{page.mftf-release}} MFTF release._ -{: style="text-align: right"} +<span style="text-align: right">_This topic was updated due to the 2.3.13 MFTF release._</span> Suites are essentially groups of tests that run in the specific conditions (preconditions and postconditions). They enable you including, excluding, and grouping tests for a customized test run when you need it. @@ -16,11 +10,7 @@ Each suite must be defined in the `<magento 2 root>/dev/tests/acceptance/tests/_ The generated tests for each suite go into a separate directory under `<magento 2 root>/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/_generated/`. By default, all generated tests are stored in the _default_ suite under `.../Magento/FunctionalTest/_generated/default/` -{% -include note.html -type="info" -content="If a test is generated into at least one custom suite, it will not appear in the _default_ suite." -%} +Note: If a test is generated into at least one custom suite, it will not appear in the _default_ suite. ## Format @@ -226,11 +216,8 @@ A suite hook with preconditions that executes once before the suite tests. It may contain test steps with any [actions] and [action groups]. -{% include note.html -type="warning" -content="Tests in the suite are not run and screenshots are not saved in case of a failure in the before hook. -To troubleshoot the failure, run the suite locally." -%} +Note: Tests in the suite are not run and screenshots are not saved in case of a failure in the before hook. +To troubleshoot the failure, run the suite locally. ### after {#after-tag} @@ -293,12 +280,12 @@ Attributes|Type|Use|Description `remove`|boolean|optional|Removing the filter during merging. <!-- Link definitions --> -[actions]: test/actions.html -[action groups]: test/action-groups.html +[actions]: test/actions.md +[action groups]: test/action-groups.md [`<after>`]: #after-tag [`<before>`]: #before-tag -[`generate:tests`]: commands/mftf.html#generatetests -[test]: test.html +[`generate:tests`]: commands/mftf.md#generatetests +[test]: test.md [`<test>`]: #test-tag [`<group>`]: #group-tag [`<module>`]: #module-tag diff --git a/docs/test.md b/docs/test.md index afcc443a0..9e104c4ea 100644 --- a/docs/test.md +++ b/docs/test.md @@ -1,12 +1,6 @@ ---- -mftf-release: 2.3.0 -redirect_from: /guides/v2.3/magento-functional-testing-framework/2.3/test.html ---- - # Test -_This topic was updated due to the {{page.mftf-release}} MFTF release._ -{: style="text-align: right"} +<span style="text-align: right">_This topic was updated due to the 2.3.13 MFTF release._</span> Test cases in the Magento Functional Testing Framework (MFTF) are defined in XML as [`<tests>`]. `<tests>` is a [Codeception test container][Codeception] that contains multiple individual tests with test metadata and before and after actions. @@ -14,18 +8,13 @@ Test cases in the Magento Functional Testing Framework (MFTF) are defined in XML MFTF `<tests>` is considered a sequence of actions with associated parameters. Any failed [assertion] within a test constitutes a failed test. -{% -include note.html -type = "info" -content='`<before>` and `<after>` hooks are not global within `<tests>`. +Note: `<before>` and `<after>` hooks are not global within `<tests>`. They only apply to the `<test>` in which they are declared. - -The steps in `<after>` are run in both successful **and** failed test runs.' -%} +The steps in `<after>` are run in both successful **and** failed test runs. The following diagram shows the structure of an MFTF test case: -{% include_relative img/test-dia.svg %} +<img src="img/test-dia.svg"/> ## Format @@ -78,7 +67,6 @@ There are several XML elements that are used in `<tests>` in the MFTF. `<test>` is a set of steps, including [actions] and [assertions][assertion]. It is a sequence of test steps that define test flow within a test method. - Attribute|Type|Use|Description ---|---|---|--- `name`|string|optional|The test identifier. @@ -102,8 +90,8 @@ Allure annotations provide metadata for reporting. `<before>` may contain these child elements: - * Any [`<action>`][actions] - * [`<actionGroup>`] +* Any [`<action>`][actions] +* [`<actionGroup>`] ### after {#after-tag} @@ -112,8 +100,8 @@ The steps are run in both successful **and** failed test runs. `<after>` may contain: - * Any [`<action>`][actions] - * [`<actionGroup>`] +* Any [`<action>`][actions] +* [`<actionGroup>`] ### actionGroup {#actiongroup-tag} @@ -148,12 +136,12 @@ See [Action groups][action group] for more information. [`<before>`]: #before-tag [`<test>`]: #test-tag [`<tests>`]: #tests-tag -[action group]: ./test/action-groups.html -[actions]: ./test/actions.html +[action group]: ./test/action-groups.md +[actions]: ./test/actions.md [Allure]: https://github.com/allure-framework/ -[Annotations]: ./test/annotations.html -[assertion]: ./test/assertions.html +[Annotations]: ./test/annotations.md +[assertion]: ./test/assertions.md [Codeception]: https://codeception.com/docs/07-AdvancedUsage -[extend]: extending.html -[merging]: ./merging.html#insert-after -[suites]: ./suite.html \ No newline at end of file +[extend]: extending.md +[merging]: ./merging.md#insert-after +[suites]: ./suite.md \ No newline at end of file diff --git a/docs/test/action-groups.md b/docs/test/action-groups.md index f93e4b594..acd90b52a 100644 --- a/docs/test/action-groups.md +++ b/docs/test/action-groups.md @@ -1,18 +1,12 @@ ---- -mftf-release: 2.3.7 -redirect_from: /guides/v2.3/magento-functional-testing-framework/2.3/test/action-groups.html ---- - # Action groups -_This topic was updated due to the {{page.mftf-release}} MFTF release._ -{: style="text-align: right"} +<span style="text-align: right">_This topic was updated due to the 2.3.13 MFTF release._</span> -In the MFTF, you can re-use a group of [actions](./actions.html){:target="_blank"}, such as logging in as an administrator or a customer, declared in an XML file when you need to perform the same sequence of actions multiple times. +In the MFTF, you can re-use a group of [actions][], such as logging in as an administrator or a customer, declared in an XML file when you need to perform the same sequence of actions multiple times. The following diagram shows the structure of an MFTF action group: -{% include_relative img/action-groups-dia.svg %} +<img src="../img/action-groups-dia.svg" /> ## Principles @@ -40,12 +34,10 @@ The XML format for the `actionGroups` declaration is: ## Example -{% raw %} - These examples build a declaration for a group of actions that grant authorization to the Admin area, and use the declaration in a test. The _Backend/ActionGroup/LoginToAdminActionGroup.xml_ `<actionGroup>` relates to the functionality of the _Backend_ module. -In [test](../test.html), the name and identifier of the `<actionGroup>` is used as a reference in the `ref` parameter, such as `ref="LoginToAdminActionGroup"`. +In [test][], the name and identifier of the `<actionGroup>` is used as a reference in the `ref` parameter, such as `ref="LoginToAdminActionGroup"`. ### Create an action group declaration @@ -128,7 +120,7 @@ Instead of adding this set of actions, use the _LoginToAdminActionGroup_ `<actio ## Data type usage -By default, an [`argument`](#argument-tag) expects an entire `entity` when the `type` value is not defined. +By default, an [`argument`][] expects an entire `entity` when the `type` value is not defined. There are cases when you use a string instead of a whole entity. For example, the following defines the replacement argument `relevantString` using a primitive data type: @@ -163,7 +155,7 @@ The `string` argument type provides a method to pass a single piece of data to t </actionGroup> ``` -The `relevantString` argument value points to the data [created](../data.html#persist-data){:target="_blank"} in the `stepKey="persistedData"` test step. +The `relevantString` argument value points to the data [created][] in the `stepKey="persistedData"` test step. `field1` is a data key of the required data string. Even with the `persistedData` data entity, the MFTF interprets the `$persistedData.field1$` value as a string. @@ -203,8 +195,6 @@ Starting with an action group such as: </actionGroup> ``` -{: .no-copy} - It can be reworked into more manageable pieces, as below. These smaller steps are easier to read, update, and reuse. ```xml @@ -231,8 +221,6 @@ It can be reworked into more manageable pieces, as below. These smaller steps ar </actionGroup> ``` -{: .no-copy} - ## Elements reference ### actionGroups {#actiongroups-tag} @@ -267,4 +255,8 @@ Attribute|Type|Use|Description `defaultValue`|string|optional|Provides a default data value. `type`|Possible values: `string`, `entity` (default).|optional|Defines the argument data type; Defaults to `entity`. -{% endraw %} +<!-- Link Definitions --> +[actions]: ./actions.md +[test]: ../test.md +[`argument`]: #argument-tag +[created]: ../data.md#persist-data \ No newline at end of file diff --git a/docs/test/actions.md b/docs/test/actions.md index 4c4525b0d..c816e58d1 100644 --- a/docs/test/actions.md +++ b/docs/test/actions.md @@ -1,12 +1,6 @@ ---- -mftf-release: 2.3.7 -redirect_from: /guides/v2.3/magento-functional-testing-framework/2.3/test/actions.html ---- - # Test actions -_This topic was updated due to the {{page.mftf-release}} MFTF release._ -{: style="text-align: right"} +<span style="text-align: right">_This topic was updated due to the 2.3.13 MFTF release._</span> Actions in the MFTF allow you to automate different scenarios of Magento user's actions. They are mostly XML implementations of [Codeception actions](http://codeception.com/docs/modules/WebDriver#Actions). @@ -30,7 +24,7 @@ This step can be referenced within the test using `conditionalClickStep1`. The value format should met the following principles: -* Must be unique within [`<test>`](../test.html#test-tag). +* Must be unique within [`<test>`](../test.md#test-tag). * Naming should be as descriptive as possible: * Describe the action performed. * Briefly describe the purpose. @@ -75,7 +69,6 @@ Example with `after`: ## Examples -{%raw%} The following example contains four actions: 1. [Open the Sign In page for a Customer](#example-step1). @@ -129,7 +122,6 @@ The customer's email is stored in the `email` parameter of the `customer` entity A required selector is stored in the `emailField` element of the `StorefrontCustomerSignInFormSection` section. This section is declared in `.../Customer/Section/StorefrontCustomerSignInFormSection.xml` file: -{: #section-code} ```xml <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" @@ -158,8 +150,7 @@ The only difference is that different data is assigned to the attributes, which ``` Here, [`<click>`](#click) performs a click on a button that can be found by the selector that is stored in the `signInAccountButton` of the `StorefrontCustomerSignInFormSection`. -See the `StorefrontCustomerSignInPage.xml` file code in [step 2](#section-code) -{%endraw%}. +See the `StorefrontCustomerSignInPage.xml` file code in [step 2](#section-code). ## Actions returning a variable @@ -174,18 +165,18 @@ The following test actions return a variable: * [grabValueFrom](#grabvaluefrom) * [executeJS](#executejs) -Learn more in [Using data returned by test actions](../data.html#use-data-returned-by-test-actions). +Learn more in [Using data returned by test actions](../data.md#use-data-returned-by-test-actions). ## Actions handling data entities -The following test actions handle data entities using [metadata](../metadata.html): +The following test actions handle data entities using [metadata](../metadata.md): * [createData](#createdata) * [deleteData](#deletedata) * [updateData](#updatedata) * [getData](#getdata) -Learn more in [Handling a REST API response](../metadata.html#rest-response). +Learn more in [Handling a REST API response](../metadata.md#rest-response). ## Reference @@ -196,7 +187,7 @@ If the description of an element does not include a link to Codeception analogue Accepts the current popup visible on the page. -See [acceptPopup docs on codeception.com](http://codeception.com/docs/modules/WebDriver#acceptPopup){:target="_blank"}. +See [acceptPopup docs on codeception.com](http://codeception.com/docs/modules/WebDriver#acceptPopup). Attribute|Type|Use|Description ---|---|---|--- @@ -216,7 +207,7 @@ Attribute|Type|Use|Description Opens the page by the URL relative to the one set in the `MAGENTO_BASE_URL` configuration variable. -See [amOnPage docs on codeception.com](http://codeception.com/docs/modules/WebDriver#amOnPage){:target="_blank"}. +See [amOnPage docs on codeception.com](http://codeception.com/docs/modules/WebDriver#amOnPage). Attribute|Type|Use|Description ---|---|---|--- @@ -237,7 +228,7 @@ Attribute|Type|Use|Description Takes the base URL and changes the subdomain. -See [amOnSubdomain docs on codeception.com](http://codeception.com/docs/modules/WebDriver#amOnSubdomain){:target="_blank"}. +See [amOnSubdomain docs on codeception.com](http://codeception.com/docs/modules/WebDriver#amOnSubdomain). Attribute|Type|Use|Description ---|---|---|--- @@ -262,7 +253,7 @@ Pre-condition: the current base URL is `https://www.magento.com`. Opens a page by the absolute URL. -See [amOnUrl docs on codeception.com](http://codeception.com/docs/modules/WebDriver#amOnUrl){:target="_blank"}. +See [amOnUrl docs on codeception.com](http://codeception.com/docs/modules/WebDriver#amOnUrl). Attribute|Type|Use|Description ---|---|---|--- @@ -281,7 +272,7 @@ Attribute|Type|Use|Description ### appendField -See [appendField docs on codeception.com](http://codeception.com/docs/modules/WebDriver#appendField){:target="_blank"}. +See [appendField docs on codeception.com](http://codeception.com/docs/modules/WebDriver#appendField). Attribute|Type|Use|Description ---|---|---|--- @@ -301,7 +292,7 @@ Attribute|Type|Use|Description ### attachFile -See [attachFile docs on codeception.com](http://codeception.com/docs/modules/WebDriver#attachFile){:target="_blank"}. +See [attachFile docs on codeception.com](http://codeception.com/docs/modules/WebDriver#attachFile). Attribute|Type|Use|Description ---|---|---|--- @@ -321,7 +312,7 @@ Attribute|Type|Use|Description ### cancelPopup -See [cancelPopup docs on codeception.com](http://codeception.com/docs/modules/WebDriver#cancelPopup){:target="_blank"}. +See [cancelPopup docs on codeception.com](http://codeception.com/docs/modules/WebDriver#cancelPopup). Attribute|Type|Use|Description ---|---|---|--- @@ -339,7 +330,7 @@ Attribute|Type|Use|Description ### checkOption -See [checkOption docs on codeception.com](http://codeception.com/docs/modules/WebDriver#checkOption){:target="_blank"}. +See [checkOption docs on codeception.com](http://codeception.com/docs/modules/WebDriver#checkOption). Attribute|Type|Use|Description ---|---|---|--- @@ -378,12 +369,12 @@ Attribute|Type|Use|Description ### click -See [click docs on codeception.com](http://codeception.com/docs/modules/WebDriver#click){:target="_blank"}. +See [click docs on codeception.com](http://codeception.com/docs/modules/WebDriver#click). Attribute|Type|Use|Description ---|---|---|--- `selector`|string|optional| The selector identifying the corresponding HTML element. -`selectorArray`|string|optional| Selects an element as a key value array. See [strict locator](http://codeception.com/docs/modules/WebDriver#locating-elements){:target="_blank"}. +`selectorArray`|string|optional| Selects an element as a key value array. See [strict locator](http://codeception.com/docs/modules/WebDriver#locating-elements). `userInput`|string|optional| Data to be sent with the click. `stepKey`|string|required| A unique identifier of the action. `skipReadiness`|boolean|optional| A flag to skip the readiness check. @@ -404,7 +395,7 @@ Attribute|Type|Use|Description ### clickWithLeftButton -See [clickWithLeftButton docs on codeception.com](http://codeception.com/docs/modules/WebDriver#clickWithLeftButton){:target="_blank"}. +See [clickWithLeftButton docs on codeception.com](http://codeception.com/docs/modules/WebDriver#clickWithLeftButton). Attribute|Type|Use|Description ---|---|---|--- @@ -436,7 +427,7 @@ Attribute|Type|Use|Description ### clickWithRightButton -See [clickWithRightButton docs on codeception.com](http://codeception.com/docs/modules/WebDriver#clickWithRightButton){:target="_blank"}. +See [clickWithRightButton docs on codeception.com](http://codeception.com/docs/modules/WebDriver#clickWithRightButton). Attribute|Type|Use|Description ---|---|---|--- @@ -486,7 +477,7 @@ Attribute|Type|Use|Description ### closeTab -See [closeTab docs on codeception.com](http://codeception.com/docs/modules/WebDriver#closeTab){:target="_blank"}. +See [closeTab docs on codeception.com](http://codeception.com/docs/modules/WebDriver#closeTab). Attribute|Type|Use|Description ---|---|---|--- @@ -546,7 +537,7 @@ Attribute|Type|Use|Description ### createData Creates an entity (for example, a category or product). -To create an entity, the MFTF makes a `POST` request to the Magento API according to the [data](../data.html) and [metadata](../metadata.html) of the entity to be created. +To create an entity, the MFTF makes a `POST` request to the Magento API according to the [data](../data.md) and [metadata](../metadata.md) of the entity to be created. Attribute|Type|Use|Description ---|---|---|--- @@ -625,7 +616,7 @@ Attribute|Type|Use|Description #### Examples -Delete the entity that was previously created using [`createData`](#createdata) in the scope of the [test](../test.html#test-tag). +Delete the entity that was previously created using [`createData`](#createdata) in the scope of the [test](../test.md#test-tag). 1. Create _SampleCategory_: @@ -641,7 +632,7 @@ Delete the entity that was previously created using [`createData`](#createdata) #### Example of existing data deletion -Delete an entity using [REST API]({{ site.gdeurl23 }}rest/bk-rest.html) request to the corresponding route: +Delete an entity using [REST API](https://devdocs.magento.com/redoc/2.3/) request to the corresponding route: ```xml <grabFromCurrentUrl regex="/^.+id\/([\d]+)/" stepKey="grabId"/> @@ -650,7 +641,7 @@ Delete an entity using [REST API]({{ site.gdeurl23 }}rest/bk-rest.html) request ### dontSee -See [the codeception.com documentation for more information about this action](http://codeception.com/docs/modules/WebDriver#dontSee){:target="_blank"}. +See [the codeception.com documentation for more information about this action](http://codeception.com/docs/modules/WebDriver#dontSee). Attribute|Type|Use|Description ---|---|---|--- @@ -671,7 +662,7 @@ Attribute|Type|Use|Description ### dontSeeCheckboxIsChecked -See [dontSeeCheckboxIsChecked docs on codeception.com](http://codeception.com/docs/modules/WebDriver#dontSeeCheckboxIsChecked){:target="_blank"}. +See [dontSeeCheckboxIsChecked docs on codeception.com](http://codeception.com/docs/modules/WebDriver#dontSeeCheckboxIsChecked). Attribute|Type|Use|Description ---|---|---|--- @@ -690,7 +681,7 @@ Attribute|Type|Use|Description ### dontSeeCookie -See [dontSeeCookie docs on codeception.com](http://codeception.com/docs/modules/WebDriver#dontSeeCookie){:target="_blank"}. +See [dontSeeCookie docs on codeception.com](http://codeception.com/docs/modules/WebDriver#dontSeeCookie). Attribute|Type|Use|Description ---|---|---|--- @@ -715,7 +706,7 @@ Attribute|Type|Use|Description ### dontSeeCurrentUrlEquals -See [dontSeeCurrentUrlEquals docs on codeception.com](http://codeception.com/docs/modules/WebDriver#dontSeeCurrentUrlEquals){:target="_blank"}. +See [dontSeeCurrentUrlEquals docs on codeception.com](http://codeception.com/docs/modules/WebDriver#dontSeeCurrentUrlEquals). Attribute|Type|Use|Description ---|---|---|--- @@ -734,7 +725,7 @@ Attribute|Type|Use|Description ### dontSeeCurrentUrlMatches -See [dontSeeCurrentUrlMatches docs on codeception.com](http://codeception.com/docs/modules/WebDriver#dontSeeCurrentUrlMatches){:target="_blank"} +See [dontSeeCurrentUrlMatches docs on codeception.com](http://codeception.com/docs/modules/WebDriver#dontSeeCurrentUrlMatches) Attribute|Type|Use|Description ---|---|---|--- @@ -753,7 +744,7 @@ Attribute|Type|Use|Description ### dontSeeElement -See [dontSeeElement docs on codeception.com](http://codeception.com/docs/modules/WebDriver#dontSeeElement){:target="_blank"}. +See [dontSeeElement docs on codeception.com](http://codeception.com/docs/modules/WebDriver#dontSeeElement). Attribute|Type|Use|Description ---|---|---|--- @@ -773,7 +764,7 @@ Attribute|Type|Use|Description ### dontSeeElementInDOM -See [dontSeeElementInDOM docs on codeception.com](http://codeception.com/docs/modules/WebDriver#dontSeeElementInDOM){:target="_blank"}. +See [dontSeeElementInDOM docs on codeception.com](http://codeception.com/docs/modules/WebDriver#dontSeeElementInDOM). Attribute|Type|Use|Description ---|---|---|--- @@ -793,7 +784,7 @@ Attribute|Type|Use|Description ### dontSeeInCurrentUrl -See [dontSeeInCurrentUrl docs on codeception.com](http://codeception.com/docs/modules/WebDriver#dontSeeInCurrentUrl){:target="_blank"}. +See [dontSeeInCurrentUrl docs on codeception.com](http://codeception.com/docs/modules/WebDriver#dontSeeInCurrentUrl). Attribute|Type|Use|Description ---|---|---|--- @@ -812,7 +803,7 @@ Attribute|Type|Use|Description ### dontSeeInField -See [dontSeeInField docs on codeception.com](http://codeception.com/docs/modules/WebDriver#dontSeeInField){:target="_blank"}. +See [dontSeeInField docs on codeception.com](http://codeception.com/docs/modules/WebDriver#dontSeeInField). Attribute|Type|Use|Description ---|---|---|--- @@ -833,7 +824,7 @@ Attribute|Type|Use|Description ### dontSeeInFormFields -See [dontSeeInFormFields docs on codeception.com](http://codeception.com/docs/modules/WebDriver#dontSeeInFormFields){:target="_blank"}. +See [dontSeeInFormFields docs on codeception.com](http://codeception.com/docs/modules/WebDriver#dontSeeInFormFields). Attribute|Type|Use|Description ---|---|---|--- @@ -853,7 +844,7 @@ Attribute|Type|Use|Description ### dontSeeInPageSource -See [dontSeeInPageSource docs on codeception.com](http://codeception.com/docs/modules/WebDriver#dontSeeInPageSource){:target="_blank"}. +See [dontSeeInPageSource docs on codeception.com](http://codeception.com/docs/modules/WebDriver#dontSeeInPageSource). Attribute|Type|Use|Description ---|---|---|--- @@ -872,7 +863,7 @@ Attribute|Type|Use|Description ### dontSeeInSource -See [dontSeeInSource docs on codeception.com](http://codeception.com/docs/modules/WebDriver#dontSeeInSource){:target="_blank"}. +See [dontSeeInSource docs on codeception.com](http://codeception.com/docs/modules/WebDriver#dontSeeInSource). Attribute|Type|Use|Description ---|---|---|--- @@ -891,7 +882,7 @@ Attribute|Type|Use|Description ### dontSeeInTitle -See [dontSeeInTitle docs on codeception.com](http://codeception.com/docs/modules/WebDriver#dontSeeInTitle){:target="_blank"}. +See [dontSeeInTitle docs on codeception.com](http://codeception.com/docs/modules/WebDriver#dontSeeInTitle). Attribute|Type|Use|Description ---|---|---|--- @@ -928,7 +919,7 @@ Attribute|Type|Use|Description ### dontSeeLink -See [dontSeeLink docs on codeception.com](http://codeception.com/docs/modules/WebDriver#dontSeeLink){:target="_blank"}. +See [dontSeeLink docs on codeception.com](http://codeception.com/docs/modules/WebDriver#dontSeeLink). Attribute|Type|Use|Description ---|---|---|--- @@ -953,7 +944,7 @@ Attribute|Type|Use|Description ### dontSeeOptionIsSelected -See [dontSeeOptionIsSelected docs on codeception.com](http://codeception.com/docs/modules/WebDriver#dontSeeOptionIsSelected){:target="_blank"}. +See [dontSeeOptionIsSelected docs on codeception.com](http://codeception.com/docs/modules/WebDriver#dontSeeOptionIsSelected). Attribute|Type|Use|Description ---|---|---|--- @@ -973,7 +964,7 @@ Attribute|Type|Use|Description ### doubleClick -See [doubleClick docs on codeception.com](http://codeception.com/docs/modules/WebDriver#doubleClick){:target="_blank"}. +See [doubleClick docs on codeception.com](http://codeception.com/docs/modules/WebDriver#doubleClick). Attribute|Type|Use|Description ---|---|---|--- @@ -992,7 +983,7 @@ Attribute|Type|Use|Description ### dragAndDrop -See [dragAndDrop docs on codeception.com](http://codeception.com/docs/modules/WebDriver#dragAndDrop){:target="_blank"}. +See [dragAndDrop docs on codeception.com](http://codeception.com/docs/modules/WebDriver#dragAndDrop). Attribute|Type|Use|Description ---|---|---|--- @@ -1019,7 +1010,7 @@ Attribute|Type|Use|Description ### executeInSelenium -See [executeInSelenium docs on codeception.com](http://codeception.com/docs/modules/WebDriver#executeInSelenium){:target="_blank"}. +See [executeInSelenium docs on codeception.com](http://codeception.com/docs/modules/WebDriver#executeInSelenium). Attribute|Type|Use|Description ---|---|---|--- @@ -1038,7 +1029,7 @@ Attribute|Type|Use|Description ### executeJS -See [executeJS docs on codeception.com](http://codeception.com/docs/modules/WebDriver#executeJS){:target="_blank"}. +See [executeJS docs on codeception.com](http://codeception.com/docs/modules/WebDriver#executeJS). Attribute|Type|Use|Description ---|---|---|--- @@ -1060,7 +1051,7 @@ To access this value you would use `{$returnTime}` in later actions. ### fillField -See [fillField docs on codeception.com](http://codeception.com/docs/modules/WebDriver#fillField){:target="_blank"}. +See [fillField docs on codeception.com](http://codeception.com/docs/modules/WebDriver#fillField). Attribute|Type|Use|Description ---|---|---|--- @@ -1135,13 +1126,13 @@ Attribute|Type|Use|Description </getData> ``` -The `ProductAttributeOptionGetter` entity must be defined in the corresponding [data `*.xml`](../data.html). +The `ProductAttributeOptionGetter` entity must be defined in the corresponding [data `*.xml`](../data.md). This action can optionally contain one or more [requiredEntity](#requiredentity) child elements. ### grabAttributeFrom -See [grabAttributeFrom docs on codeception.com](http://codeception.com/docs/modules/WebDriver#grabAttributeFrom){:target="_blank"}. +See [grabAttributeFrom docs on codeception.com](http://codeception.com/docs/modules/WebDriver#grabAttributeFrom). Attribute|Type|Use|Description ---|---|---|--- @@ -1162,7 +1153,7 @@ To access this value, use `{$grabAttributeFromInput}` in later actions. --> ### grabCookie -See [grabCookie docs on codeception.com](http://codeception.com/docs/modules/WebDriver#grabCookie){:target="_blank"}. +See [grabCookie docs on codeception.com](http://codeception.com/docs/modules/WebDriver#grabCookie). Attribute|Type|Use|Description ---|---|---|--- @@ -1189,7 +1180,7 @@ To access this value, use `{$grabCookieExampleDomain}` in later actions. --> ### grabFromCurrentUrl -See [grabFromCurrentUrl docs on codeception.com](http://codeception.com/docs/modules/WebDriver#grabFromCurrentUrl){:target="_blank"}.. +See [grabFromCurrentUrl docs on codeception.com](http://codeception.com/docs/modules/WebDriver#grabFromCurrentUrl).. Attribute|Type|Use|Description ---|---|---|--- @@ -1209,7 +1200,7 @@ To access this value, use `{$grabFromCurrentUrl}` in later actions. --> ### grabMultiple -See [grabMultiple docs on codeception.com](http://codeception.com/docs/modules/WebDriver#grabMultiple){:target="_blank"}.. +See [grabMultiple docs on codeception.com](http://codeception.com/docs/modules/WebDriver#grabMultiple).. Attribute|Type|Use|Description ---|---|---|--- @@ -1236,7 +1227,7 @@ To access this value, use `{$grabAllLinks}` in later actions. --> ### grabPageSource -See [grabPageSource docs on codeception.com](http://codeception.com/docs/modules/WebDriver#grabPageSource){:target="_blank"}. +See [grabPageSource docs on codeception.com](http://codeception.com/docs/modules/WebDriver#grabPageSource). Attribute|Type|Use|Description ---|---|---|--- @@ -1255,7 +1246,7 @@ To access this value, use `{$grabPageSource}` in later actions. --> ### grabTextFrom -See [grabTextFrom docs on codeception.com](http://codeception.com/docs/modules/WebDriver#grabTextFrom){:target="_blank"}. +See [grabTextFrom docs on codeception.com](http://codeception.com/docs/modules/WebDriver#grabTextFrom). Attribute|Type|Use|Description ---|---|---|--- @@ -1275,7 +1266,7 @@ To access this value, use `{$grabTitle}` in later actions. --> ### grabValueFrom -See [grabValueFrom docs on codeception.com](https://codeception.com/docs/modules/WebDriver#grabValueFrom){:target="_blank"}. +See [grabValueFrom docs on codeception.com](https://codeception.com/docs/modules/WebDriver#grabValueFrom). Attribute|Type|Use|Description ---|---|---|--- @@ -1296,7 +1287,7 @@ To access this value, use `{$grabInputName}` in later actions. --> ### loadSessionSnapshot -See [loadSessionSnapshot docs on codeception.com](http://codeception.com/docs/modules/WebDriver#loadSessionSnapshot){:target="_blank"}. +See [loadSessionSnapshot docs on codeception.com](http://codeception.com/docs/modules/WebDriver#loadSessionSnapshot). Attribute|Type|Use|Description ---|---|---|--- @@ -1336,7 +1327,7 @@ Attribute|Type|Use|Description ### makeScreenshot -See [makeScreenshot docs on codeception.com](http://codeception.com/docs/modules/WebDriver#makeScreenshot){:target="_blank"}. +See [makeScreenshot docs on codeception.com](http://codeception.com/docs/modules/WebDriver#makeScreenshot). Attribute|Type|Use|Description ---|---|---|--- @@ -1346,10 +1337,7 @@ Attribute|Type|Use|Description `before`|string|optional| `stepKey` of action that must be executed next. `after`|string|optional| `stepKey` of preceding action. -{% include note.html - type="info" - content="The makeScreenshot action does not automatically add the screenshot to Allure reports." - %} +Note that the makeScreenshot action does not automatically add the screenshot to Allure reports. #### Example @@ -1358,12 +1346,12 @@ Attribute|Type|Use|Description <makeScreenshot userInput="example" stepKey="screenshotPage"/> ``` -{:.bs-callout .bs-callout-info} -This action does not add a screenshot to the Allure [report](../reporting.html). +<span class=".bs-callout .bs-callout-info"> +This action does not add a screenshot to the Allure [report](../reporting.md).</span> ### maximizeWindow -See [maximizeWindow docs on codeception.com](http://codeception.com/docs/modules/WebDriver#maximizeWindow){:target="_blank"}. +See [maximizeWindow docs on codeception.com](http://codeception.com/docs/modules/WebDriver#maximizeWindow). Attribute|Type|Use|Description ---|---|---|--- @@ -1381,7 +1369,7 @@ Attribute|Type|Use|Description ### moveBack -See [moveBack docs on codeception.com](http://codeception.com/docs/modules/WebDriver#moveBack){:target="_blank"}. +See [moveBack docs on codeception.com](http://codeception.com/docs/modules/WebDriver#moveBack). Attribute|Type|Use|Description ---|---|---|--- @@ -1399,7 +1387,7 @@ Attribute|Type|Use|Description ### moveForward -See [moveForward docs on codeception.com](http://codeception.com/docs/modules/WebDriver#moveForward){:target="_blank"}.. +See [moveForward docs on codeception.com](http://codeception.com/docs/modules/WebDriver#moveForward).. Attribute|Type|Use|Description ---|---|---|--- @@ -1415,7 +1403,7 @@ Attribute|Type|Use|Description ### moveMouseOver -See [moveMouseOver docs on codeception.com](http://codeception.com/docs/modules/WebDriver#moveMouseOver){:target="_blank"}. +See [moveMouseOver docs on codeception.com](http://codeception.com/docs/modules/WebDriver#moveMouseOver). Attribute|Type|Use|Description ---|---|---|--- @@ -1462,7 +1450,7 @@ Attribute|Type|Use|Description ### openNewTab -See [openNewTab docs on codeception.com](http://codeception.com/docs/modules/WebDriver#openNewTab){:target="_blank"}. +See [openNewTab docs on codeception.com](http://codeception.com/docs/modules/WebDriver#openNewTab). Attribute|Type|Use|Description ---|---|---|--- @@ -1492,7 +1480,7 @@ Attribute|Type|Use|Description ### pauseExecution -See [pauseExecution docs on codeception.com](http://codeception.com/docs/modules/WebDriver#pauseExecution){:target="_blank"}. +See [pauseExecution docs on codeception.com](http://codeception.com/docs/modules/WebDriver#pauseExecution). Attribute|Type|Use|Description ---|---|---|--- @@ -1510,7 +1498,7 @@ Attribute|Type|Use|Description ### performOn -See [performOn docs on codeception.com](http://codeception.com/docs/modules/WebDriver#performOn){:target="_blank"}. +See [performOn docs on codeception.com](http://codeception.com/docs/modules/WebDriver#performOn). Attribute|Type|Use|Description ---|---|---|--- @@ -1523,7 +1511,7 @@ Attribute|Type|Use|Description ### pressKey -See [pressKey docs on codeception.com](http://codeception.com/docs/modules/WebDriver#pressKey){:target="_blank"}. +See [pressKey docs on codeception.com](http://codeception.com/docs/modules/WebDriver#pressKey). Attribute|Type|Use|Description ---|---|---|--- @@ -1552,7 +1540,7 @@ To press more than one key at a time, wrap the keys in secondary `[]`. ### reloadPage -See [reloadPage docs on codeception.com](http://codeception.com/docs/modules/WebDriver#reloadPage){:target="_blank"}. +See [reloadPage docs on codeception.com](http://codeception.com/docs/modules/WebDriver#reloadPage). Attribute|Type|Use|Description ---|---|---|--- @@ -1585,7 +1573,7 @@ Attribute|Type|Use|Description ### resetCookie -See [resetCookie docs on codeception.com](http://codeception.com/docs/modules/WebDriver#resetCookie){:target="_blank"}. +See [resetCookie docs on codeception.com](http://codeception.com/docs/modules/WebDriver#resetCookie). Attribute|Type|Use|Description ---|---|---|--- @@ -1610,7 +1598,7 @@ Attribute|Type|Use|Description ### resizeWindow -See [resizeWindow docs on codeception.com](http://codeception.com/docs/modules/WebDriver#resizeWindow){:target="_blank"}. +See [resizeWindow docs on codeception.com](http://codeception.com/docs/modules/WebDriver#resizeWindow). Attribute|Type|Use|Description ---|---|---|--- @@ -1630,7 +1618,7 @@ Attribute|Type|Use|Description ### saveSessionSnapshot -See [saveSessionSnapshot docs on codeception.com](http://codeception.com/docs/modules/WebDriver#saveSessionSnapshot){:target="_blank"}. +See [saveSessionSnapshot docs on codeception.com](http://codeception.com/docs/modules/WebDriver#saveSessionSnapshot). Attribute|Type|Use|Description ---|---|---|--- @@ -1649,7 +1637,7 @@ Attribute|Type|Use|Description ### scrollTo -See [scrollTo docs on codeception.com](http://codeception.com/docs/modules/WebDriver#scrollTo){:target="_blank"}. +See [scrollTo docs on codeception.com](http://codeception.com/docs/modules/WebDriver#scrollTo). Attribute|Type|Use|Description ---|---|---|--- @@ -1725,7 +1713,7 @@ On this test step the MFTF: ### see -See [see docs on codeception.com](http://codeception.com/docs/modules/WebDriver#see){:target="_blank"}. +See [see docs on codeception.com](http://codeception.com/docs/modules/WebDriver#see). Attribute|Type|Use|Description ---|---|---|--- @@ -1746,7 +1734,7 @@ Attribute|Type|Use|Description ### seeCheckboxIsChecked -See [seeCheckboxIsChecked docs on codeception.com](http://codeception.com/docs/modules/WebDriver#seeCheckboxIsChecked){:target="_blank"}. +See [seeCheckboxIsChecked docs on codeception.com](http://codeception.com/docs/modules/WebDriver#seeCheckboxIsChecked). Attribute|Type|Use|Description ---|---|---|--- @@ -1765,7 +1753,7 @@ Attribute|Type|Use|Description ### seeCookie -See [seeCookie docs on codeception.com](http://codeception.com/docs/modules/WebDriver#seeCookie){:target="_blank"}. +See [seeCookie docs on codeception.com](http://codeception.com/docs/modules/WebDriver#seeCookie). Attribute|Type|Use|Description ---|---|---|--- @@ -1790,7 +1778,7 @@ Attribute|Type|Use|Description ### seeCurrentUrlEquals -See [seeCurrentUrlEquals docs on codeception.com](http://codeception.com/docs/modules/WebDriver#seeCurrentUrlEquals){:target="_blank"}. +See [seeCurrentUrlEquals docs on codeception.com](http://codeception.com/docs/modules/WebDriver#seeCurrentUrlEquals). Attribute|Type|Use|Description ---|---|---|--- @@ -1809,7 +1797,7 @@ Attribute|Type|Use|Description ### seeCurrentUrlMatches -See [seeCurrentUrlMatches docs on codeception.com](http://codeception.com/docs/modules/WebDriver#seeCurrentUrlMatches){:target="_blank"}. +See [seeCurrentUrlMatches docs on codeception.com](http://codeception.com/docs/modules/WebDriver#seeCurrentUrlMatches). Attribute|Type|Use|Description ---|---|---|--- @@ -1828,7 +1816,7 @@ Attribute|Type|Use|Description ### seeElement -See [seeElement docs on codeception.com](http://codeception.com/docs/modules/WebDriver#seeElement){:target="_blank"}. +See [seeElement docs on codeception.com](http://codeception.com/docs/modules/WebDriver#seeElement). Attribute|Type|Use|Description ---|---|---|--- @@ -1849,7 +1837,7 @@ Attribute|Type|Use|Description ### seeElementInDOM -See [seeElementInDOM docs on codeception.com](http://codeception.com/docs/modules/WebDriver#seeElementInDOM){:target="_blank"}. +See [seeElementInDOM docs on codeception.com](http://codeception.com/docs/modules/WebDriver#seeElementInDOM). Attribute|Type|Use|Description ---|---|---|--- @@ -1869,7 +1857,7 @@ Attribute|Type|Use|Description ### seeInCurrentUrl -See [seeInCurrentUrl docs on codeception.com](http://codeception.com/docs/modules/WebDriver#seeInCurrentUrl){:target="_blank"}. +See [seeInCurrentUrl docs on codeception.com](http://codeception.com/docs/modules/WebDriver#seeInCurrentUrl). Attribute|Type|Use|Description ---|---|---|--- @@ -1888,7 +1876,7 @@ Attribute|Type|Use|Description ### seeInField -See [seeInField docs on codeception.com](http://codeception.com/docs/modules/WebDriver#seeInField){:target="_blank"}. +See [seeInField docs on codeception.com](http://codeception.com/docs/modules/WebDriver#seeInField). Attribute|Type|Use|Description ---|---|---|--- @@ -1909,7 +1897,7 @@ Attribute|Type|Use|Description ### seeInFormFields -See [seeInFormFields docs on codeception.com](http://codeception.com/docs/modules/WebDriver#seeInFormFields){:target="_blank"}. +See [seeInFormFields docs on codeception.com](http://codeception.com/docs/modules/WebDriver#seeInFormFields). Attribute|Type|Use|Description ---|---|---|--- @@ -1929,7 +1917,7 @@ Attribute|Type|Use|Description ### seeInPageSource -See [seeInPageSource docs on codeception.com](http://codeception.com/docs/modules/WebDriver#seeInPageSource){:target="_blank"}. +See [seeInPageSource docs on codeception.com](http://codeception.com/docs/modules/WebDriver#seeInPageSource). Attribute|Type|Use|Description ---|---|---|--- @@ -1948,7 +1936,7 @@ Attribute|Type|Use|Description ### seeInPopup -See [seeInPopup docs on codeception.com](http://codeception.com/docs/modules/WebDriver#seeInPopup){:target="_blank"}. +See [seeInPopup docs on codeception.com](http://codeception.com/docs/modules/WebDriver#seeInPopup). Attribute|Type|Use|Description ---|---|---|--- @@ -1967,7 +1955,7 @@ Attribute|Type|Use|Description ### seeInSource -See [seeInSource docs on codeception.com](http://codeception.com/docs/modules/WebDriver#seeInSource){:target="_blank"}. +See [seeInSource docs on codeception.com](http://codeception.com/docs/modules/WebDriver#seeInSource). Attribute|Type|Use|Description ---|---|---|--- @@ -1986,7 +1974,7 @@ Attribute|Type|Use|Description ### seeInTitle -See [seeInTitle docs on codeception.com](http://codeception.com/docs/modules/WebDriver#seeInTitle){:target="_blank"}. +See [seeInTitle docs on codeception.com](http://codeception.com/docs/modules/WebDriver#seeInTitle). Attribute|Type|Use|Description ---|---|---|--- @@ -2005,7 +1993,7 @@ Attribute|Type|Use|Description ### seeLink -See [seeLink docs on codeception.com](http://codeception.com/docs/modules/WebDriver#seeLink){:target="_blank"}. +See [seeLink docs on codeception.com](http://codeception.com/docs/modules/WebDriver#seeLink). Attribute|Type|Use|Description ---|---|---|--- @@ -2030,7 +2018,7 @@ Attribute|Type|Use|Description ### seeNumberOfElements -See [seeNumberOfElements docs on codeception.com](http://codeception.com/docs/modules/WebDriver#seeNumberOfElements){:target="_blank"}. +See [seeNumberOfElements docs on codeception.com](http://codeception.com/docs/modules/WebDriver#seeNumberOfElements). Attribute|Type|Use|Description ---|---|---|--- @@ -2056,7 +2044,7 @@ Attribute|Type|Use|Description ### seeOptionIsSelected -See [seeOptionIsSelected docs on codeception.com](http://codeception.com/docs/modules/WebDriver#seeOptionIsSelected){:target="_blank"}. +See [seeOptionIsSelected docs on codeception.com](http://codeception.com/docs/modules/WebDriver#seeOptionIsSelected). Attribute|Type|Use|Description ---|---|---|--- @@ -2076,7 +2064,7 @@ Attribute|Type|Use|Description ### selectOption -See [selectOption docs on codeception.com](http://codeception.com/docs/modules/WebDriver#selectOption){:target="_blank"}. +See [selectOption docs on codeception.com](http://codeception.com/docs/modules/WebDriver#selectOption). Attribute|Type|Use|Description ---|---|---|--- @@ -2121,7 +2109,7 @@ It contains a child element `<array>` where you specify the options that must be ### setCookie -See [setCookie docs on codeception.com](http://codeception.com/docs/modules/WebDriver#setCookie){:target="_blank"}. +See [setCookie docs on codeception.com](http://codeception.com/docs/modules/WebDriver#setCookie). Attribute|Type|Use|Description ---|---|---|--- @@ -2142,7 +2130,7 @@ Attribute|Type|Use|Description ### submitForm -See [submitForm docs on codeception.com](http://codeception.com/docs/modules/WebDriver#submitForm){:target="_blank"}. +See [submitForm docs on codeception.com](http://codeception.com/docs/modules/WebDriver#submitForm). Attribute|Type|Use|Description ---|---|---|--- @@ -2163,7 +2151,7 @@ Attribute|Type|Use|Description ### switchToIFrame -See [switchToIFrame docs on codeception.com](http://codeception.com/docs/modules/WebDriver#switchToIFrame){:target="_blank"}. +See [switchToIFrame docs on codeception.com](http://codeception.com/docs/modules/WebDriver#switchToIFrame). Attribute|Type|Use|Description ---|---|---|--- @@ -2183,7 +2171,7 @@ Attribute|Type|Use|Description ### switchToNextTab -See [switchToNextTab docs on codeception.com](http://codeception.com/docs/modules/WebDriver#switchToNextTab){:target="_blank"}. +See [switchToNextTab docs on codeception.com](http://codeception.com/docs/modules/WebDriver#switchToNextTab). Attribute|Type|Use|Description ---|---|---|--- @@ -2207,7 +2195,7 @@ Attribute|Type|Use|Description ### switchToPreviousTab -See [switchToPreviousTab docs on codeception.com](http://codeception.com/docs/modules/WebDriver#switchToPreviousTab){:target="_blank"}. +See [switchToPreviousTab docs on codeception.com](http://codeception.com/docs/modules/WebDriver#switchToPreviousTab). Attribute|Type|Use|Description ---|---|---|--- @@ -2231,7 +2219,7 @@ Attribute|Type|Use|Description ### switchToWindow -See [switchToWindow docs on codeception.com](http://codeception.com/docs/modules/WebDriver#switchToWindow){:target="_blank"}. +See [switchToWindow docs on codeception.com](http://codeception.com/docs/modules/WebDriver#switchToWindow). Attribute|Type|Use|Description ---|---|---|--- @@ -2250,7 +2238,7 @@ Attribute|Type|Use|Description ### typeInPopup -See [typeInPopup docs on codeception.com](http://codeception.com/docs/modules/WebDriver#typeInPopup){:target="_blank"}. +See [typeInPopup docs on codeception.com](http://codeception.com/docs/modules/WebDriver#typeInPopup). Attribute|Type|Use|Description ---|---|---|--- @@ -2269,7 +2257,7 @@ Attribute|Type|Use|Description ### uncheckOption -See [uncheckOption docs on codeception.com](http://codeception.com/docs/modules/WebDriver#uncheckOption){:target="_blank"}. +See [uncheckOption docs on codeception.com](http://codeception.com/docs/modules/WebDriver#uncheckOption). Attribute|Type|Use|Description ---|---|---|--- @@ -2288,7 +2276,7 @@ Attribute|Type|Use|Description ### unselectOption -See [unselectOption docs on codeception.com](http://codeception.com/docs/modules/WebDriver#unselectOption){:target="_blank"}. +See [unselectOption docs on codeception.com](http://codeception.com/docs/modules/WebDriver#unselectOption). Attribute|Type|Use|Description ---|---|---|--- @@ -2342,7 +2330,7 @@ This action can optionally contain one or more [requiredEntity](#requiredentity) ### wait -See [wait docs on codeception.com](http://codeception.com/docs/modules/WebDriver#wait){:target="_blank"}. +See [wait docs on codeception.com](http://codeception.com/docs/modules/WebDriver#wait). Attribute|Type|Use|Description ---|---|---|--- @@ -2380,7 +2368,7 @@ Attribute|Type|Use|Description ### waitForElementChange -See [waitForElementChange docs on codeception.com](http://codeception.com/docs/modules/WebDriver#waitForElementChange){:target="_blank"}. +See [waitForElementChange docs on codeception.com](http://codeception.com/docs/modules/WebDriver#waitForElementChange). Attribute|Type|Use|Description ---|---|---|--- @@ -2401,7 +2389,7 @@ Attribute|Type|Use|Description ### waitForElement -See [waitForElement docs on codeception.com](http://codeception.com/docs/modules/WebDriver#waitForElement){:target="_blank"}. +See [waitForElement docs on codeception.com](http://codeception.com/docs/modules/WebDriver#waitForElement). Attribute|Type|Use|Description ---|---|---|--- @@ -2421,7 +2409,7 @@ Attribute|Type|Use|Description ### waitForElementNotVisible -See [waitForElementNotVisible docs on codeception.com](http://codeception.com/docs/modules/WebDriver#waitForElementNotVisible){:target="_blank"}. +See [waitForElementNotVisible docs on codeception.com](http://codeception.com/docs/modules/WebDriver#waitForElementNotVisible). Attribute|Type|Use|Description ---|---|---|--- @@ -2441,7 +2429,7 @@ Attribute|Type|Use|Description ### waitForElementVisible -See [waitForElementVisible docs on codeception.com](http://codeception.com/docs/modules/WebDriver#waitForElementVisible){:target="_blank"}. +See [waitForElementVisible docs on codeception.com](http://codeception.com/docs/modules/WebDriver#waitForElementVisible). Attribute|Type|Use|Description ---|---|---|--- @@ -2461,7 +2449,7 @@ Attribute|Type|Use|Description ### waitForJS -See [waitForJS docs on codeception.com](http://codeception.com/docs/modules/WebDriver#waitForJS){:target="_blank"}. +See [waitForJS docs on codeception.com](http://codeception.com/docs/modules/WebDriver#waitForJS). Attribute|Type|Use|Description ---|---|---|--- @@ -2483,9 +2471,9 @@ Attribute|Type|Use|Description Wait for all Magento loading overlays to disappear. -{: .bs-callout .bs-callout-info } +<span class=".bs-callout .bs-callout-info"> The CSS class for loading masks is not used consistently throughout Magento. -Therefore, this convenience function tries to wait for various specific selectors. +Therefore, this convenience function tries to wait for various specific selectors.</span> ```config # Wait for these classes to not be visible @@ -2529,6 +2517,7 @@ Attribute|Type|Use|Description <!-- Wait up to 30 seconds for the current page to fully load before continuing. --> <waitForPageLoad stepKey="waitForPageLoad"/> ``` + ### waitForPwaElementNotVisible Waits up to the given `time` for a PWA Element to disappear from the screen. @@ -2548,6 +2537,7 @@ Attribute|Type|Use|Description <!-- Wait for the PWA element to disappear. --> <waitForPwaElementNotVisible time="1" stepKey="waitForPwaElementNotVisible"/> ``` + ### waitForPwaElementVisible Waits up to the given 'time' for a PWA Element to appear on the screen. @@ -2570,7 +2560,7 @@ Attribute|Type|Use|Description ### waitForText -See [waitForText docs on codeception.com](http://codeception.com/docs/modules/WebDriver#waitForText){:target="_blank"}. +See [waitForText docs on codeception.com](http://codeception.com/docs/modules/WebDriver#waitForText). Attribute|Type|Use|Description ---|---|---|--- diff --git a/docs/test/annotations.md b/docs/test/annotations.md index 673db0e84..93326ade4 100644 --- a/docs/test/annotations.md +++ b/docs/test/annotations.md @@ -1,12 +1,6 @@ ---- -mftf-release: 2.3.6 -redirect_from: /guides/v2.3/magento-functional-testing-framework/2.3/test/annotations.html ---- - # Annotations -_This topic was updated due to the {{page.mftf-release}} MFTF release._ -{: style="text-align: right"} +<span style="text-align: right">_This topic was updated due to the 2.3.13 MFTF release._</span> Annotations are essentially comments in the code. In PHP, they all are marked by a preceding `@` symbol. @@ -24,6 +18,7 @@ If multiple annotation values are supported and required each value requires a s - Tests must contain all of the following annotations: stories, title, description, severity. Recommended use cases of the annotation types: + - [stories] - report grouping, a set of tests that verify a story. - [title] - description of the test purpose. - [group] - general functionality grouping. @@ -133,7 +128,7 @@ Attribute|Type|Use|Acceptable values Use the `<skip>` element to skip a test. It contains one or more child elements `<issueId>` to specify one or more issues that cause the test skipping. -##### issueId +#### issueId This element under `<skip>` is required at least once and contains references to issues that cause the test to be skipped. @@ -231,7 +226,7 @@ Attribute|Type|Use [setup instructions in Allure]: https://github.com/allure-framework/allure1/wiki/Test-Case-ID [severity]: #severity [stories]: #stories -[suite]: ../suite.html -[tests]: ../test.html +[suite]: ../suite.md +[tests]: ../test.md [title]: #title [skip]: #skip diff --git a/docs/test/assertions.md b/docs/test/assertions.md index c5c453273..3944ba826 100644 --- a/docs/test/assertions.md +++ b/docs/test/assertions.md @@ -1,32 +1,27 @@ ---- -mftf-release: 2.3.0 -redirect_from: /guides/v2.3/magento-functional-testing-framework/2.3/test/assertions.html ---- - # Assertions -_This topic was updated due to the {{page.mftf-release}} MFTF release._ -{: style="text-align: right"} +<span style="text-align: right">_This topic was updated due to the 2.3.13 MFTF release._</span> -Assertions serve to pass or fail the [test](../test.html#test-tag) if a condition is not met. These assertions will look familiar to you if you've used any other testing framework, like PHPUnit. +Assertions serve to pass or fail the [test](../test.md#test-tag) if a condition is not met. These assertions will look familiar to you if you've used any other testing framework, like PHPUnit. -All assertions contain the same [common actions attributes](./actions.html#common-attributes): `stepKey`, `before`, and `after`. +All assertions contain the same [common actions attributes](./actions.md#common-attributes): `stepKey`, `before`, and `after`. Most assertions contain a `message` attribute that specifies the text of an informational message to help you identify the cause of the failure. ## Principles -The [principles for actions](../test.html#principles) are also applicable to assertions. +The [principles for actions](../test.md#principles) are also applicable to assertions. Assertion actions have nested self-descriptive elements, `<expectedResult>` and `<actualResult>`. These elements contain a result type and a value: + * `type` - * `const` (default) - * `int` - * `float` - * `bool` - * `string` - * `variable` - * `array` + * `const` (default) + * `int` + * `float` + * `bool` + * `string` + * `variable` + * `array` * `value` If `variable` is used, the test transforms the corresponding value to `$variable`. Use the `stepKey` of a test, that returns the value you want to use, in assertions: @@ -95,7 +90,7 @@ It must be in typical array format like `[1,2,3,4,5]` or `[alpha, brontosaurus, ### assertArrayHasKey -See [assertArrayHasKey docs on codeception.com](http://codeception.com/docs/modules/Asserts#assertArrayHasKey){:target='_blank'} +See [assertArrayHasKey docs on codeception.com](http://codeception.com/docs/modules/Asserts#assertArrayHasKey) Attribute|Type|Use|Description ---|---|---|--- @@ -110,7 +105,7 @@ Attribute|Type|Use|Description ### assertArrayNotHasKey -See [assertArrayNotHasKey docs on codeception.com](http://codeception.com/docs/modules/Asserts#assertArrayNotHasKey){:target='_blank'}. +See [assertArrayNotHasKey docs on codeception.com](http://codeception.com/docs/modules/Asserts#assertArrayNotHasKey). Attribute|Type|Use|Description ---|---|---|--- @@ -125,7 +120,7 @@ Attribute|Type|Use|Description ### assertArraySubset -See [assertArraySubset docs on codeception.com](http://codeception.com/docs/modules/Asserts#assertArraySubset){:target='_blank'}. +See [assertArraySubset docs on codeception.com](http://codeception.com/docs/modules/Asserts#assertArraySubset). Attribute|Type|Use|Description ---|---|---|--- @@ -141,7 +136,7 @@ Attribute|Type|Use|Description ### assertContains -See [assertContains docs on codeception.com](http://codeception.com/docs/modules/Asserts#assertContains){:target='_blank'}. +See [assertContains docs on codeception.com](http://codeception.com/docs/modules/Asserts#assertContains). Attribute|Type|Use|Description ---|---|---|--- @@ -156,7 +151,7 @@ Attribute|Type|Use|Description ### assertCount -See [assertCount docs on codeception.com](http://codeception.com/docs/modules/Asserts#assertCount){:target='_blank'}. +See [assertCount docs on codeception.com](http://codeception.com/docs/modules/Asserts#assertCount). Attribute|Type|Use|Description ---|---|---|--- @@ -171,7 +166,7 @@ Attribute|Type|Use|Description ### assertEmpty -See [assertEmpty docs on codeception.com](http://codeception.com/docs/modules/Asserts#assertEmpty){:target='_blank'}. +See [assertEmpty docs on codeception.com](http://codeception.com/docs/modules/Asserts#assertEmpty). Attribute|Type|Use|Description ---|---|---|--- @@ -184,7 +179,7 @@ Attribute|Type|Use|Description ### assertEquals -See [assertEquals docs on codeception.com](http://codeception.com/docs/modules/Asserts#assertEquals){:target='_blank'}. +See [assertEquals docs on codeception.com](http://codeception.com/docs/modules/Asserts#assertEquals). Attribute|Type|Use|Description ---|---|---|--- @@ -200,7 +195,7 @@ Attribute|Type|Use|Description ### assertFalse -See [assertFalse docs on codeception.com](http://codeception.com/docs/modules/Asserts#assertFalse){:target='_blank'}. +See [assertFalse docs on codeception.com](http://codeception.com/docs/modules/Asserts#assertFalse). Attribute|Type|Use|Description ---|---|---|--- @@ -213,7 +208,7 @@ Attribute|Type|Use|Description ### assertFileExists -See [assertFileExists docs on codeception.com](http://codeception.com/docs/modules/Asserts#assertFileExists){:target='_blank'}. +See [assertFileExists docs on codeception.com](http://codeception.com/docs/modules/Asserts#assertFileExists). Attribute|Type|Use|Description ---|---|---|--- @@ -226,7 +221,7 @@ Attribute|Type|Use|Description ### assertFileNotExists -See [assertFileNotExists docs on codeception.com](http://codeception.com/docs/modules/Asserts#assertFileNotExists){:target='_blank'}. +See [assertFileNotExists docs on codeception.com](http://codeception.com/docs/modules/Asserts#assertFileNotExists). Attribute|Type|Use|Description ---|---|---|--- @@ -239,7 +234,7 @@ Attribute|Type|Use|Description ### assertGreaterOrEquals -See [assertGreaterOrEquals docs on codeception.com](http://codeception.com/docs/modules/Asserts#assertGreaterOrEquals){:target='_blank'}. +See [assertGreaterOrEquals docs on codeception.com](http://codeception.com/docs/modules/Asserts#assertGreaterOrEquals). Attribute|Type|Use|Description ---|---|---|--- @@ -254,7 +249,7 @@ Attribute|Type|Use|Description ### assertGreaterThan -See [assertGreaterThan docs on codeception.com](http://codeception.com/docs/modules/Asserts#assertGreaterThan){:target='_blank'}. +See [assertGreaterThan docs on codeception.com](http://codeception.com/docs/modules/Asserts#assertGreaterThan). Attribute|Type|Use|Description ---|---|---|--- @@ -269,7 +264,7 @@ Attribute|Type|Use|Description ### assertGreaterThanOrEqual -See [assertGreaterThanOrEqual docs on codeception.com](http://codeception.com/docs/modules/Asserts#assertGreaterThanOrEqual){:target='_blank'}. +See [assertGreaterThanOrEqual docs on codeception.com](http://codeception.com/docs/modules/Asserts#assertGreaterThanOrEqual). Attribute|Type|Use|Description ---|---|---|--- @@ -284,7 +279,7 @@ Attribute|Type|Use|Description ### assertInstanceOf -See [assertInstanceOf docs on codeception.com](http://codeception.com/docs/modules/Asserts#assertInstanceOf){:target='_blank'}. +See [assertInstanceOf docs on codeception.com](http://codeception.com/docs/modules/Asserts#assertInstanceOf). Attribute|Type|Use|Description ---|---|---|--- @@ -299,7 +294,7 @@ Attribute|Type|Use|Description ### assertInternalType -See [assertInternalType docs on codeception.com](http://codeception.com/docs/modules/Asserts#assertInternalType){:target='_blank'}. +See [assertInternalType docs on codeception.com](http://codeception.com/docs/modules/Asserts#assertInternalType). Attribute|Type|Use|Description ---|---|---|--- @@ -314,7 +309,7 @@ Attribute|Type|Use|Description ### assertIsEmpty -See [assertIsEmpty docs on codeception.com](http://codeception.com/docs/modules/Asserts#assertIsEmpty){:target='_blank'}. +See [assertIsEmpty docs on codeception.com](http://codeception.com/docs/modules/Asserts#assertIsEmpty). Attribute|Type|Use|Description ---|---|---|--- @@ -327,7 +322,7 @@ Attribute|Type|Use|Description ### assertLessOrEquals -See [assertLessOrEquals docs on codeception.com](http://codeception.com/docs/modules/Asserts#assertLessOrEquals){:target='_blank'}. +See [assertLessOrEquals docs on codeception.com](http://codeception.com/docs/modules/Asserts#assertLessOrEquals). Attribute|Type|Use|Description ---|---|---|--- @@ -342,7 +337,7 @@ Attribute|Type|Use|Description ### assertLessThan -See [assertLessThan docs on codeception.com](http://codeception.com/docs/modules/Asserts#assertLessThan){:target='_blank'}. +See [assertLessThan docs on codeception.com](http://codeception.com/docs/modules/Asserts#assertLessThan). Attribute|Type|Use|Description ---|---|---|--- @@ -357,7 +352,7 @@ Attribute|Type|Use|Description ### assertLessThanOrEqual -See [assertLessThanOrEqual docs on codeception.com](http://codeception.com/docs/modules/Asserts#assertLessThanOrEqual){:target='_blank'}. +See [assertLessThanOrEqual docs on codeception.com](http://codeception.com/docs/modules/Asserts#assertLessThanOrEqual). Attribute|Type|Use|Description ---|---|---|--- @@ -372,7 +367,7 @@ Attribute|Type|Use|Description ### assertNotContains -See [assertNotContains docs on codeception.com](http://codeception.com/docs/modules/Asserts#assertNotContains){:target='_blank'}. +See [assertNotContains docs on codeception.com](http://codeception.com/docs/modules/Asserts#assertNotContains). Attribute|Type|Use|Description ---|---|---|--- @@ -387,7 +382,7 @@ Attribute|Type|Use|Description ### assertNotEmpty -See [assertNotEmpty docs on codeception.com](http://codeception.com/docs/modules/Asserts#assertNotEmpty){:target='_blank'}. +See [assertNotEmpty docs on codeception.com](http://codeception.com/docs/modules/Asserts#assertNotEmpty). Attribute|Type|Use|Description ---|---|---|--- @@ -400,7 +395,7 @@ Attribute|Type|Use|Description ### assertNotEquals -See [assertNotEquals docs on codeception.com](http://codeception.com/docs/modules/Asserts#assertNotEquals){:target='_blank'}. +See [assertNotEquals docs on codeception.com](http://codeception.com/docs/modules/Asserts#assertNotEquals). Attribute|Type|Use|Description ---|---|---|--- @@ -416,7 +411,7 @@ Attribute|Type|Use|Description ### assertNotInstanceOf -See [assertNotInstanceOf docs on codeception.com](http://codeception.com/docs/modules/Asserts#assertNotInstanceOf){:target='_blank'}. +See [assertNotInstanceOf docs on codeception.com](http://codeception.com/docs/modules/Asserts#assertNotInstanceOf). Attribute|Type|Use|Description ---|---|---|--- @@ -431,7 +426,7 @@ Attribute|Type|Use|Description ### assertNotNull -See [assertNotNull docs on codeception.com](http://codeception.com/docs/modules/Asserts#assertNotNull){:target='_blank'}. +See [assertNotNull docs on codeception.com](http://codeception.com/docs/modules/Asserts#assertNotNull). Attribute|Type|Use|Description ---|---|---|--- @@ -444,7 +439,7 @@ Attribute|Type|Use|Description ### assertNotRegExp -See [assertNotRegExp docs on codeception.com](http://codeception.com/docs/modules/Asserts#assertNotRegExp){:target='_blank'}. +See [assertNotRegExp docs on codeception.com](http://codeception.com/docs/modules/Asserts#assertNotRegExp). Attribute|Type|Use|Description ---|---|---|--- @@ -459,7 +454,7 @@ Attribute|Type|Use|Description ### assertNotSame -See [assertNotSame docs on codeception.com](http://codeception.com/docs/modules/Asserts#assertNotSame){:target='_blank'}. +See [assertNotSame docs on codeception.com](http://codeception.com/docs/modules/Asserts#assertNotSame). Attribute|Type|Use|Description ---|---|---|--- @@ -474,7 +469,7 @@ Attribute|Type|Use|Description ### assertNull -See [assertNull docs on codeception.com](http://codeception.com/docs/modules/Asserts#assertNull){:target='_blank'}. +See [assertNull docs on codeception.com](http://codeception.com/docs/modules/Asserts#assertNull). Attribute|Type|Use|Description ---|---|---|--- @@ -487,7 +482,7 @@ Attribute|Type|Use|Description ### assertRegExp -See [assertRegExp docs on codeception.com](http://codeception.com/docs/modules/Asserts#assertRegExp){:target='_blank'}. +See [assertRegExp docs on codeception.com](http://codeception.com/docs/modules/Asserts#assertRegExp). Attribute|Type|Use|Description ---|---|---|--- @@ -502,7 +497,7 @@ Attribute|Type|Use|Description ### assertSame -See [assertSame docs on codeception.com](http://codeception.com/docs/modules/Asserts#assertSame){:target='_blank'}. +See [assertSame docs on codeception.com](http://codeception.com/docs/modules/Asserts#assertSame). Attribute|Type|Use|Description ---|---|---|--- @@ -517,7 +512,7 @@ Attribute|Type|Use|Description ### assertStringStartsNotWith -See [assertStringStartsNotWith docs on codeception.com](http://codeception.com/docs/modules/Asserts#assertStringStartsNotWith){:target='_blank'}. +See [assertStringStartsNotWith docs on codeception.com](http://codeception.com/docs/modules/Asserts#assertStringStartsNotWith). Attribute|Type|Use|Description ---|---|---|--- @@ -532,7 +527,7 @@ Attribute|Type|Use|Description ### assertStringStartsWith -See [assertStringStartsWith docs on codeception.com](http://codeception.com/docs/modules/Asserts#assertStringStartsWith){:target='_blank'}. +See [assertStringStartsWith docs on codeception.com](http://codeception.com/docs/modules/Asserts#assertStringStartsWith). Attribute|Type|Use|Description ---|---|---|--- @@ -547,7 +542,7 @@ Attribute|Type|Use|Description ### assertTrue -See [assertTrue docs on codeception.com](http://codeception.com/docs/modules/Asserts#assertTrue){:target='_blank'}. +See [assertTrue docs on codeception.com](http://codeception.com/docs/modules/Asserts#assertTrue). Attribute|Type|Use|Description ---|---|---|--- @@ -560,7 +555,7 @@ Attribute|Type|Use|Description ### expectException -See [expectException docs on codeception.com](http://codeception.com/docs/modules/WebDriver#expectException){:target='_blank'}. +See [expectException docs on codeception.com](http://codeception.com/docs/modules/WebDriver#expectException). Attribute|Type|Use|Description ---|---|---|--- @@ -574,7 +569,7 @@ Attribute|Type|Use|Description ### fail -See [fail docs on codeception.com](http://codeception.com/docs/modules/WebDriver#fail){:target='_blank'}. +See [fail docs on codeception.com](http://codeception.com/docs/modules/WebDriver#fail). Attribute|Type|Use|Description ---|---|---|--- diff --git a/docs/tips-tricks.md b/docs/tips-tricks.md index 9ba09f29b..411823870 100644 --- a/docs/tips-tricks.md +++ b/docs/tips-tricks.md @@ -1,10 +1,8 @@ ---- -title: MFTF Tips and tricks ---- +# Tips and Tricks -Sometimes, little changes can make a big difference in your project. Here are some test writing tips to keep everything running smoothly. +<span style="text-align: right">_This topic was updated due to the 2.3.13 MFTF release._</span> -<!-- {% raw %} --> +Sometimes, little changes can make a big difference in your project. Here are some test writing tips to keep everything running smoothly. ## Actions and action groups @@ -17,8 +15,9 @@ In the bad example we see two parameters being passed into the selector with lit **Why?** The next person maintaining the test or extending it may not be able to understand what the parameters are referencing. -{:style="color:green"} +<span stype="color:green"> Good +</span> ```xml <test> @@ -37,8 +36,9 @@ Good </actionGroup> ``` -{:style="color:red"} +<span style="color:red"> Bad +</span> ```xml <test> @@ -54,8 +54,9 @@ In the bad example, we perform some heavy UI steps first. **Why?** If something goes wrong there, then the critical `magentoCLI` commands may not get a chance to run, leaving Magento configured incorrectly for any upcoming tests. -{:style="color:green"} +<span stype="color:green"> Good: +</span> ```xml <after> @@ -73,8 +74,9 @@ Good: </after> ``` -{:style="color:red"} +<span style="color:red"> Bad: +</span> ```xml <after> @@ -106,15 +108,17 @@ And for `seeElement` it will output something like this: There is a subtle distinction: The first is a failure but it is the desired result: a 'positive failure'. The second is a proper result of the action. -{:style="color:green"} +<span stype="color:green"> Good: +</span> ```xml <see selector="//div[@data-element='content']//p" userInput="SOME EXPECTED TEXT" stepKey="seeSlide1ContentStorefront"/> ``` -{:style="color:red"} +<span style="color:red"> Bad: +</span> ```xml <seeElement selector="//div[@data-element='content']//p[.='SOME EXPECTED TEXT']" stepKey="seeSlide1ContentStorefront"/> @@ -124,8 +128,9 @@ Bad: Whenever possible, specify a `defaultValue` for action group arguments. -{:style="color:green"} +<span stype="color:green"> GOOD: +</span> ```xml <actionGroup name="StorefrontAssertProductImagesOnProductPageActionGroup"> @@ -143,8 +148,9 @@ GOOD: </actionGroup> ``` -{:style="color:red"} +<span style="color:red"> BAD: +</span> ```xml <actionGroup name="StorefrontAssertProductImagesOnProductPageActionGroup"> @@ -170,8 +176,9 @@ Build your tests using action groups, even if an action group contains a single Extending a single action group will update all tests that use this group. This improves maintainability as multiple instances of a failure can be fixed with a single action group update. -{:style="color:green"} +<span stype="color:green"> GOOD: +</span> ```xml <test name="NavigateClamberWatchEntityTest"> @@ -197,8 +204,9 @@ GOOD: </test> ``` -{:style="color:red"} +<span style="color:red"> BAD: +</span> ```xml <test name="NavigateClamberWatchEntityTest"> @@ -226,8 +234,9 @@ Do not use numbers to make a `stepKey` unique. **Why?** This helps with readability and clarity. -{:style="color:green"} +<span stype="color:green"> GOOD: +</span> ```xml <click selector="{{StorefrontNavigationSection.topCategory(SimpleSubCategory.name)}}" stepKey="clickSimpleSubCategoryLink" /> @@ -243,8 +252,9 @@ GOOD: <waitForPageLoad stepKey="waitForCustomSimpleProductPageLoad" /> ``` -{:style="color:red"} +<span style="color:red"> BAD: +</span> ```xml <click selector="{{StorefrontNavigationSection.topCategory(SimpleSubCategory.name)}}" stepKey="clickCategoryLink1" /> @@ -298,15 +308,17 @@ Example: </div> ``` -{:style="color:green"} +<span stype="color:green"> GOOD: +</span> ```xml <element name="productName" type="input" selector="*[data-index='product-details'] input[name='product[name]']"/> ``` -{:style="color:red"} +<span style="color:red"> BAD: +</span> ```xml <element name="productName" type="input" selector=".admin__field[data-index=name] input"/> @@ -319,8 +331,9 @@ When possible, select the first parent tag and then specify the desired element **Why?** Elements that are overly specific are less flexible and may fail if unexpected DOM changes occur. It also reduces the amount of the DOM it needs to parse. - {:style="color:green"} + <span stype="color:green"> GOOD: +</span> ```html form[name='myform'] > input[name='firstname'] @@ -328,8 +341,9 @@ GOOD: //*[@id='container'][@class='dashboard-title'] ``` - {:style="color:red"} + <span style="color:red"> BAD: +</span> ```html input[name='firstname'] @@ -344,15 +358,17 @@ BAD: Use descriptive variable names to increase readability. **Why?** It makes the code easier to follow and update. - {:style="color:green"} + <span stype="color:green"> GOOD: +</span> ```xml <element name="storeName" type="checkbox" selector="//label[contains(text(),'{{storeName}}')]" parameterized="true"/> ``` -{:style="color:red"} +<span style="color:red"> BAD: +</span> ```xml <element name="storeName" type="checkbox" selector="//label[contains(text(),'{{var1}}')]" parameterized="true"/> @@ -363,20 +379,20 @@ BAD: When working with input type `checkbox`, do not use the `click` action; use `checkOption` or `uncheckOption` instead. **Why?** A click does not make it clear what the ending state will be; it will simply toggle the current state. Using the proper actions will ensure the expected state of the checkbox. -{:style="color:green"} +<span stype="color:green"> GOOD: +</span> ```xml <checkOption selector="{{ProductInWebsitesSection.website('Second Website')}}" stepKey="selectSecondWebsite"/> <uncheckOption selector="{{ProductInWebsitesSection.website('Second Website')}}" stepKey="unselectSecondWebsite"/> ``` -{:style="color:red"} +<span style="color:red"> BAD: +</span> ```xml <click selector="{{ProductInWebsitesSection.website('Second Website')}}" stepKey="selectSecondWebsite"/> <click selector="{{ProductInWebsitesSection.website('Second Website')}}" stepKey="unselectSecondWebsite"/> -``` - -<!-- {% endraw %} --> +``` \ No newline at end of file diff --git a/docs/troubleshooting.md b/docs/troubleshooting.md index 9c90573e0..3bc5df2cb 100644 --- a/docs/troubleshooting.md +++ b/docs/troubleshooting.md @@ -1,12 +1,6 @@ ---- -mftf-release: 2.0.2 -redirect_from: /guides/v2.3/magento-functional-testing-framework/2.3/troubleshooting.html ---- - # Troubleshooting -_This topic was updated due to the {{page.mftf-release}} MFTF release._ -{: style="text-align: right"} +<span style="text-align: right">_This topic was updated due to the 2.3.13 MFTF release._</span> Having a little trouble with the MFTF? See some common errors and fixes below. diff --git a/docs/update.md b/docs/update.md index edaaefea3..45723996c 100644 --- a/docs/update.md +++ b/docs/update.md @@ -1,14 +1,13 @@ ---- -mftf-release: 2.3.0 -redirect_from: /guides/v2.3/magento-functional-testing-framework/2.3/update.html ---- - # Update the Magento Functional Testing Framework -_This topic was updated due to the {{page.mftf-release}} MFTF release._ -{: style="text-align: right"} +<span style="text-align: right">_This topic was updated due to the 2.3.13 MFTF release._</span> + +<div class="bs-callout bs-callout-info"> +<a href="https://devdocs.magento.com/mftf/2.3/introduction.html#find-version">Find out your version</a> of the MFTF. -{% include_relative include/note-2.2-docs.md %} +The latest Magento 2.3 release supports MFTF 2.3.13. +The latest Magento 2.2 release supports MFTF 2.3.8. +</div> Magento tests and the framework are stored in different repositories. @@ -78,5 +77,5 @@ To update the MFTF from the previous minor version: <!-- Link Definitions --> [Changelog]: https://github.com/magento/magento2-functional-testing-framework/blob/master/CHANGELOG.md -[WYSIWYG settings]: getting-started.html#wysiwyg-settings -[Security settings]: getting-started.html#security-settings \ No newline at end of file +[WYSIWYG settings]: getting-started.md#wysiwyg-settings +[Security settings]: getting-started.md#security-settings \ No newline at end of file diff --git a/docs/versioning.md b/docs/versioning.md index f74b47b2f..08b2f3777 100644 --- a/docs/versioning.md +++ b/docs/versioning.md @@ -1,6 +1,4 @@ ---- -title: Versioning Policy for MFTF ---- +# Versioning This documemt describes the versioning policy for the Magento Functional Testing Framework (MFTF), including the version numbering schema. From 0698d92fee682afce041d943fc09535489d65106 Mon Sep 17 00:00:00 2001 From: Donald Booth <dobooth@adobe.com> Date: Wed, 6 Mar 2019 16:45:21 -0600 Subject: [PATCH 03/18] Updated images to markdown. --- docs/data.md | 2 +- docs/metadata.md | 2 +- docs/page.md | 2 +- docs/section.md | 2 +- docs/test.md | 2 +- docs/test/action-groups.md | 4 +--- docs/tips-tricks.md | 7 +++++-- 7 files changed, 11 insertions(+), 10 deletions(-) diff --git a/docs/data.md b/docs/data.md index 384a39105..305cf15ac 100644 --- a/docs/data.md +++ b/docs/data.md @@ -5,7 +5,7 @@ The MFTF enables you to specify and use `<data>` entities defined in XML. Default `<data>` entities are provided for use and as templates for entity creation and manipulation. The following diagram shows the XML structure of an MFTF data object: -<img src="img/data-dia.svg" /> +![MFTF Data Object](img/data-dia.svg) ## Supply data to test by reference to a data entity diff --git a/docs/metadata.md b/docs/metadata.md index cb87ffa68..05f685e4b 100644 --- a/docs/metadata.md +++ b/docs/metadata.md @@ -39,7 +39,7 @@ When a test step requires handling the specified data entity, the MFTF performs - Stores a response and provides access to its data using MFTF variables syntax in XML. The following diagram demonstrates the XML structure of a metadata file: -<img src="img/metadata-dia.svg" /> +![Structure of metadata](img/metadata-dia.svg) ## Format {#format} diff --git a/docs/page.md b/docs/page.md index 04559917c..2b3470e1a 100644 --- a/docs/page.md +++ b/docs/page.md @@ -19,7 +19,7 @@ Two types of pages are available: The following diagram shows the XML structure of an MFTF page: -<img src="img/page-dia.svg" /> +![XML Structure of MFTF Page](img/page-dia.svg) ## Format diff --git a/docs/section.md b/docs/section.md index 804cf6d57..dfd1bf1f2 100644 --- a/docs/section.md +++ b/docs/section.md @@ -21,7 +21,7 @@ Substitutable values in the test can be of the following formats: The following diagram shows the XML structure of an MFTF section: -<img src="img/section-dia.svg%" /> +![XML Structure of MFTF section](img/section-dia.svg) ## Format diff --git a/docs/test.md b/docs/test.md index 9e104c4ea..c8e33c670 100644 --- a/docs/test.md +++ b/docs/test.md @@ -14,7 +14,7 @@ The steps in `<after>` are run in both successful **and** failed test runs. The following diagram shows the structure of an MFTF test case: -<img src="img/test-dia.svg"/> +![Structure of MFTF test case](img/test-dia.svg) ## Format diff --git a/docs/test/action-groups.md b/docs/test/action-groups.md index acd90b52a..d69f597ca 100644 --- a/docs/test/action-groups.md +++ b/docs/test/action-groups.md @@ -1,12 +1,10 @@ # Action groups -<span style="text-align: right">_This topic was updated due to the 2.3.13 MFTF release._</span> - In the MFTF, you can re-use a group of [actions][], such as logging in as an administrator or a customer, declared in an XML file when you need to perform the same sequence of actions multiple times. The following diagram shows the structure of an MFTF action group: -<img src="../img/action-groups-dia.svg" /> +![Structure of MFTF action group](../img/action-groups-dia.svg) ## Principles diff --git a/docs/tips-tricks.md b/docs/tips-tricks.md index 411823870..b04e7cb1d 100644 --- a/docs/tips-tricks.md +++ b/docs/tips-tricks.md @@ -331,7 +331,7 @@ When possible, select the first parent tag and then specify the desired element **Why?** Elements that are overly specific are less flexible and may fail if unexpected DOM changes occur. It also reduces the amount of the DOM it needs to parse. - <span stype="color:green"> + <span style="color:green"> GOOD: </span> @@ -383,6 +383,8 @@ When working with input type `checkbox`, do not use the `click` action; use `che GOOD: </span> +<!--{% raw %}--> + ```xml <checkOption selector="{{ProductInWebsitesSection.website('Second Website')}}" stepKey="selectSecondWebsite"/> <uncheckOption selector="{{ProductInWebsitesSection.website('Second Website')}}" stepKey="unselectSecondWebsite"/> @@ -395,4 +397,5 @@ BAD: ```xml <click selector="{{ProductInWebsitesSection.website('Second Website')}}" stepKey="selectSecondWebsite"/> <click selector="{{ProductInWebsitesSection.website('Second Website')}}" stepKey="unselectSecondWebsite"/> -``` \ No newline at end of file +``` +<!--{% endraw %}--> From 705236c8f38ae28c02db9b930a53b92dd835a5aa Mon Sep 17 00:00:00 2001 From: Donald Booth <dobooth@adobe.com> Date: Thu, 7 Mar 2019 15:04:30 -0600 Subject: [PATCH 04/18] Replaced {% raw %} blocks. --- docs/best-practices.md | 38 +++++++++++++------------ docs/credentials.md | 4 +++ docs/data.md | 6 ++-- docs/extending.md | 4 +++ docs/merging.md | 4 +++ docs/page.md | 4 +++ docs/section.md | 4 +++ docs/section/locator-functions.md | 4 +++ docs/section/parameterized-selectors.md | 4 +++ docs/test/action-groups.md | 4 +++ docs/test/actions.md | 4 +++ docs/tips-tricks.md | 4 +-- 12 files changed, 62 insertions(+), 22 deletions(-) diff --git a/docs/best-practices.md b/docs/best-practices.md index 3a03921ac..e3800b8ea 100644 --- a/docs/best-practices.md +++ b/docs/best-practices.md @@ -1,7 +1,5 @@ # Best practices -<span style="text-align: right">_This topic was updated due to the 2.3.13 MFTF release._</span> - Check out our best practices below to ensure you are getting the absolute most out of the Magento Functional Testing Framework. ## Action group @@ -109,10 +107,14 @@ Do not use them for static elements. BAD: </span> +<!-- {% raw %} --> + ``` xml <element name="relatedProductSectionText" type="text" selector=".fieldset-wrapper.admin__fieldset-section[data-index='{{productType}}']" parameterized="true"/> ``` +<!-- {% endraw %} --> + <span class="color:green"> GOOD: </span> @@ -146,19 +148,19 @@ Since the configurable product module could be disabled, this approach is more r <!-- Link definitions --> -[`<after>`]: test/actions.html#before-and-after -[`<before>`]: test/actions.html#before-and-after -[`<comment>`]: test/actions.html#comment -[`<createData>`]: test/actions.html#createdata -[`<deleteData>`]: test/actions.html#deletedata -[`<wait>`]: test/actions.html#wait -[`<waitForElement>`]: test/actions.html#waitforelement -[`<waitForElementVisible>`]: test/actions.html#waitforelementvisible -[`<waitForLoadingMaskToDisappear>`]: test/actions.html#waitforloadingmasktodisappear -[Action group]: test/action-groups.html -[annotations]: test/annotations.html -[entity]: data.html -[extension]: extending.html -[merging]: merging.html -[parameterized selectors]: section/parameterized-selectors.html -[sections]: section.html +[`<after>`]: test/actions.md#before-and-after +[`<before>`]: test/actions.md#before-and-after +[`<comment>`]: test/actions.md#comment +[`<createData>`]: test/actions.md#createdata +[`<deleteData>`]: test/actions.md#deletedata +[`<wait>`]: test/actions.md#wait +[`<waitForElement>`]: test/actions.md#waitforelement +[`<waitForElementVisible>`]: test/actions.md#waitforelementvisible +[`<waitForLoadingMaskToDisappear>`]: test/actions.md#waitforloadingmasktodisappear +[Action group]: test/action-groups.md +[annotations]: test/annotations.md +[entity]: data.md +[extension]: extending.md +[merging]: merging.md +[parameterized selectors]: section/parameterized-selectors.md +[sections]: section.md diff --git a/docs/credentials.md b/docs/credentials.md index 70fac4da6..a8a4ab347 100644 --- a/docs/credentials.md +++ b/docs/credentials.md @@ -64,6 +64,8 @@ carriers_usps_password=Lmgxvrq89uPwECeV .... ``` +<!-- {% raw %} --> + ## Use credentials in a test Access the data defined in the `.credentials` file using the [`fillField`][] action with the `userInput` attribute. @@ -78,6 +80,8 @@ For example: <fillField stepKey="FillApiToken" selector=".api-token" userInput="{{_CREDS.my_data_key}}" /> ``` +<!-- {% raw %} --> + ## Implementation details The generated tests do not contain credentials values. diff --git a/docs/data.md b/docs/data.md index 305cf15ac..27b6c1ffc 100644 --- a/docs/data.md +++ b/docs/data.md @@ -1,12 +1,12 @@ # Input testing data -<span style="text-align: right">_This topic was updated due to the 2.3.13 MFTF release._</span> - The MFTF enables you to specify and use `<data>` entities defined in XML. Default `<data>` entities are provided for use and as templates for entity creation and manipulation. The following diagram shows the XML structure of an MFTF data object: ![MFTF Data Object](img/data-dia.svg) +<!-- {% raw %} --> + ## Supply data to test by reference to a data entity Test steps requiring `<data>` input in an action, like filling a field with a string, may reference an attribute from a data entity: @@ -166,6 +166,8 @@ The following is an example of a call in test: <fillField selector="{{AdminCategoryBasicFieldSection.categoryNameInput}}" userInput="{{_defaultCategory.name}}" stepKey="enterCategoryName"/> ``` +<!-- {% endraw %} --> + This action inputs data from the `name` of the `_defaultCategory` entity (for example, `simpleCategory598742365`) into the field with the locator defined in the selector of the `categoryNameInput` element of the `AdminCategoryBasicFieldSection`. ## Reference diff --git a/docs/extending.md b/docs/extending.md index 7395ece61..a4dfec449 100644 --- a/docs/extending.md +++ b/docs/extending.md @@ -20,6 +20,8 @@ Unlike merging, the parent test (or action group) will still exist after the tes ### Update a test step +<!-- {% raw %} --> + __Use case__: Create two similar tests with different `url` (`"{{AdminCategoryPage.url}}"` and `"{{OtherCategoryPage.url}}"`) in a test step. > Test with "extends": @@ -281,6 +283,8 @@ Add a new test `VerifyProductCount` that asserts the count of products: </actionGroups> ``` +<!-- {% endraw %} --> + ## Extending data Extend data to reuse entities in your module. diff --git a/docs/merging.md b/docs/merging.md index f4e9073a9..c7821f87e 100644 --- a/docs/merging.md +++ b/docs/merging.md @@ -45,6 +45,8 @@ Learn more about running tests with different options using [`mftf`] or [`codece Skip the `AdminLoginTest` test in the `.../Backend/Test/AdminLoginTest.xml` file while merging with the `.../Foo/Test/AdminLoginTest.xml` file: +<!-- {% raw %} --> + ```xml <tests ...> <test name="AdminLoginTest"> @@ -304,6 +306,8 @@ The controls change drastically in the B2B version, so it was abstracted to an a </actionGroup> ``` +<!-- {% raw %} --> + ## Merge pages Use [page] merging to add or remove [sections] in your module. diff --git a/docs/page.md b/docs/page.md index 2b3470e1a..b0542aab0 100644 --- a/docs/page.md +++ b/docs/page.md @@ -13,6 +13,8 @@ Avoid hard-coded location selectors from tests to increase the maintainability a Two types of pages are available: +<!-- {% raw %} --> + - Page with `url` declared as a constant string or [explicit page] - In a test it is called in a format like `{{NameOfPage.url}}`, where `NameOfPage` is a value of `name` in the corresponding page declaration `*.xml` file. - Page with `url` declared as a string with one or more variables or [parameterized page] - In a test it is called using a format like `{{NameOfPage.url(var1, var2, ...)}}`, where `var1, var2` etc. are parameters that will be substituted in the `url` of the corresponding `<page>` declaration. @@ -148,6 +150,8 @@ Attributes|Type|Use|Description `<page>` may contain several [`<section>`] elements. +<!-- {% endraw %} --> + ### section {#section-tag} `<section>` contains the sequence of UI elements. diff --git a/docs/section.md b/docs/section.md index dfd1bf1f2..cf8668c32 100644 --- a/docs/section.md +++ b/docs/section.md @@ -6,6 +6,8 @@ A `<section>` is a reusable part of a [`<page>`](./page.md) and is the standard A `<section>` can define: +<!-- {% raw %} --> + - An explicit element that has a selector equal to the constant string. Example: `selector="#add_root_category_button"` - A parameterized element that contains substitutable values in the selector. Example: `selector="#element .{{var1}} .{{var2}}"`. @@ -132,6 +134,8 @@ The test step that covers the use case: ... ``` +<!-- {% endraw %} --> + Whenever the `signIn` button is used in a test, the MFTF will add a 30 second `waitForPageLoad` action immediately after the `click`. <!-- Link definitions --> diff --git a/docs/section/locator-functions.md b/docs/section/locator-functions.md index 78df44f55..40fa5a206 100644 --- a/docs/section/locator-functions.md +++ b/docs/section/locator-functions.md @@ -20,6 +20,8 @@ When using the `locatorFunction`, omit `Locator::` for code simplicity: An element's `locatorFunction` can also be parameterized the same way as [parameterized selectors][]: +<!-- {% raw %} --> + ```xml <element name="simpleLocatorTwoParam" type="button" locatorFunction="contains({{arg1}}, {{arg2}})" parameterized="true"/> ``` @@ -37,6 +39,8 @@ Given the above element definitions, you call the elements in a test just like a </test> ``` +<!-- {% endraw %} --> + <!-- Link Definitions --> [Locator functions]: http://codeception.com/docs/reference/Locator [section]: ../section.md diff --git a/docs/section/parameterized-selectors.md b/docs/section/parameterized-selectors.md index b9a9530eb..adbe42773 100644 --- a/docs/section/parameterized-selectors.md +++ b/docs/section/parameterized-selectors.md @@ -30,6 +30,8 @@ Add your selector in the `selector=""` attribute: </section> ``` +<!-- {% raw %} --> + ### Selector with single variable For the parameterized part of the selector, add `{{var1}}` to represent the first piece of data that you want to replace: @@ -142,6 +144,8 @@ Add the second or third parameters, that you'd like to pass to the selector, sep </test> ``` +<!-- {% endraw %} --> + Any data can be used in parameterized elements, as well as entered in test actions: * `_defaultCategory.is_active` is a reference to `<data key="is_active">` in `<entity name="_defaultCategory" ... ></entity>` in the corresponding `.../Data/*.xml`. diff --git a/docs/test/action-groups.md b/docs/test/action-groups.md index d69f597ca..0743481f6 100644 --- a/docs/test/action-groups.md +++ b/docs/test/action-groups.md @@ -54,6 +54,8 @@ To create the `<actionGroup>` declaration: </actionGroups> ``` +<!-- {% raw %} --> + 1. Add actions to the `actionGroup` arguments: ```xml @@ -219,6 +221,8 @@ It can be reworked into more manageable pieces, as below. These smaller steps ar </actionGroup> ``` +<!-- {% endraw %} --> + ## Elements reference ### actionGroups {#actiongroups-tag} diff --git a/docs/test/actions.md b/docs/test/actions.md index c816e58d1..d6315dbce 100644 --- a/docs/test/actions.md +++ b/docs/test/actions.md @@ -69,6 +69,8 @@ Example with `after`: ## Examples +<!-- {% raw %} --> + The following example contains four actions: 1. [Open the Sign In page for a Customer](#example-step1). @@ -149,6 +151,8 @@ The only difference is that different data is assigned to the attributes, which <click selector="{{StorefrontCustomerSignInFormSection.signInAccountButton}}" stepKey="clickSignInAccountButton"/> ``` +<!-- {% endraw %} --> + Here, [`<click>`](#click) performs a click on a button that can be found by the selector that is stored in the `signInAccountButton` of the `StorefrontCustomerSignInFormSection`. See the `StorefrontCustomerSignInPage.xml` file code in [step 2](#section-code). diff --git a/docs/tips-tricks.md b/docs/tips-tricks.md index b04e7cb1d..d0f31bf4a 100644 --- a/docs/tips-tricks.md +++ b/docs/tips-tricks.md @@ -19,6 +19,8 @@ In the bad example we see two parameters being passed into the selector with lit Good </span> +<!-- {% raw %} --> + ```xml <test> <actionGroup ref="VerifyOptionInProductStorefront" stepKey="verifyConfigurableOption" after="AssertProductInStorefrontProductPage"> @@ -383,8 +385,6 @@ When working with input type `checkbox`, do not use the `click` action; use `che GOOD: </span> -<!--{% raw %}--> - ```xml <checkOption selector="{{ProductInWebsitesSection.website('Second Website')}}" stepKey="selectSecondWebsite"/> <uncheckOption selector="{{ProductInWebsitesSection.website('Second Website')}}" stepKey="unselectSecondWebsite"/> From 540a3b5570b9a69c526a44501ea44a3e298a8c51 Mon Sep 17 00:00:00 2001 From: Donald Booth <dobooth@adobe.com> Date: Thu, 7 Mar 2019 15:45:20 -0600 Subject: [PATCH 05/18] Removed version note. --- docs/commands/codeception.md | 1 - docs/commands/mftf.md | 2 +- docs/configuration.md | 2 -- docs/data.md | 4 +++- docs/extending.md | 2 -- docs/getting-started.md | 2 -- docs/introduction.md | 4 +--- docs/merging.md | 2 -- docs/metadata.md | 6 +++--- docs/page.md | 2 -- docs/reporting.md | 4 +++- docs/section.md | 2 -- docs/section/locator-functions.md | 1 - docs/section/parameterized-selectors.md | 1 - docs/suite.md | 10 ++++++---- docs/test.md | 6 +++--- docs/test/actions.md | 1 - docs/test/annotations.md | 1 - docs/test/assertions.md | 1 - docs/tips-tricks.md | 3 +-- docs/troubleshooting.md | 2 -- docs/update.md | 2 -- docs/versioning.md | 2 +- 23 files changed, 22 insertions(+), 41 deletions(-) diff --git a/docs/commands/codeception.md b/docs/commands/codeception.md index e9694523f..2c248d665 100644 --- a/docs/commands/codeception.md +++ b/docs/commands/codeception.md @@ -1,6 +1,5 @@ # CLI commands: vendor/bin/codecept -<span style="text-align: right">_This topic was updated due to the 2.3.13 MFTF release._</span> <span class="bs-callout bs-callout-warning"> We do not recommend using Codeception commands directly as they can break the MFTF basic workflow. diff --git a/docs/commands/mftf.md b/docs/commands/mftf.md index 7bdbf2f29..78ab22723 100644 --- a/docs/commands/mftf.md +++ b/docs/commands/mftf.md @@ -169,7 +169,7 @@ The command that encodes this complex configuration: vendor/bin/mftf generate:tests --tests "{\r\n\"tests\":[\r\n\"general_test1\",\r\n\"general_test2\",\r\n\"general_test3\"\r\n],\r\n\"suites\":{\r\n\"sample\":[\r\n\"suite_test1\"\r\n],\r\n\"sample2\":null\r\n}\r\n}" ``` -Note: The strings must be escaped and surrounded in quotes. +Note that the strings must be escaped and surrounded in quotes. ### `generate:suite` diff --git a/docs/configuration.md b/docs/configuration.md index dfd5f7134..854a77bdf 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -1,7 +1,5 @@ # Configuration -<span style="text-align: right">_This topic was updated due to the 2.3.13 MFTF release._</span> - The `*.env` file provides additional configuration for the Magento Functional Testing Framework (MFTF). To run the MFTF on your Magento testing instance, specify the basic configuration values. Advanced users can create custom configurations based on requirements and environment. diff --git a/docs/data.md b/docs/data.md index 27b6c1ffc..c5845c1d4 100644 --- a/docs/data.md +++ b/docs/data.md @@ -63,7 +63,9 @@ In this example: * `email` is a data key of the entity. The corresponding value will be assigned to `userInput` as a result. -Note: As of MFTF 2.3.6, you no longer need to differentiate between scopes (a test, a hook, or a suite) for persisted data when referencing it in tests. +<div class="bs-callout bs-callout-info"> +As of MFTF 2.3.6, you no longer need to differentiate between scopes (a test, a hook, or a suite) for persisted data when referencing it in tests. +</div> The MFTF now stores the persisted data and attempts to retrieve it using the combination of `stepKey` and the scope of where it has been called. The current scope is preferred, then widening to _test > hook > suite_ or _hook > test > suite_. diff --git a/docs/extending.md b/docs/extending.md index a4dfec449..b86d8b247 100644 --- a/docs/extending.md +++ b/docs/extending.md @@ -1,7 +1,5 @@ # Extending -<span style="text-align: right">_This topic was updated due to the 2.3.13 MFTF release._</span> - There are cases when you need to create many tests that are very similar to each other. For example, only one or two parameters (for example, URL) might vary between tests. To avoid copy-pasting and to save some time the Magento Functional Testing Framework (MFTF) enables you to extend test components such as [test], [data], and [action group]. diff --git a/docs/getting-started.md b/docs/getting-started.md index d1dfda2fb..0e3bf05db 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -1,7 +1,5 @@ # Getting started -<span style="text-align: right">_This topic was updated due to the 2.3.13 MFTF release._</span> - <div class="bs-callout bs-callout-info"> <a href="https://devdocs.magento.com/mftf/2.3/introduction.html#find-version">Find out your version</a> of the MFTF. diff --git a/docs/introduction.md b/docs/introduction.md index fbb86ad8c..74142d5bd 100644 --- a/docs/introduction.md +++ b/docs/introduction.md @@ -1,7 +1,5 @@ # Introduction to the Magento Functional Testing Framework version 2.3 -<span style="text-align: right">_This topic was updated due to the 2.3.13 MFTF release._</span> - <a href="https://devdocs.magento.com/mftf/2.3/introduction.html#find-version">Find out your version</a> of the MFTF. The latest Magento 2.3 release supports MFTF 2.3.13. @@ -134,4 +132,4 @@ Follow the [MFTF project] and [contribute on Github]. <!-- Link definitions --> [contribute on Github]: https://github.com/magento/magento2-functional-testing-framework/blob/master/.github/CONTRIBUTING.md [Functional Testing Framework]: https://devdocs.magento.com/guides/v2.3/mtf/mtf_introduction.html -[MFTF project]: https://github.com/magento/magento2-functional-testing-framework +[MFTF project]: https://github.com/magento/magento2-functional-testing-framework \ No newline at end of file diff --git a/docs/merging.md b/docs/merging.md index c7821f87e..8762e6b58 100644 --- a/docs/merging.md +++ b/docs/merging.md @@ -1,7 +1,5 @@ # Merging -<span style="text-align: right">_This topic was updated due to the 2.3.13 MFTF release._</span> - The MFTF allows you to merge test components defined in XML files, such as: - [`<tests>`][] diff --git a/docs/metadata.md b/docs/metadata.md index 05f685e4b..178871ea1 100644 --- a/docs/metadata.md +++ b/docs/metadata.md @@ -1,7 +1,5 @@ # Metadata -<span style="text-align: right">_This topic was updated due to the 2.3.13 MFTF release._</span> - In this topic we talk about handling entities that you need in your tests (such as categories, products, wish lists, and similar) using the MFTF. Using data handling actions like [`createData`], [`deleteData`], [`updateData`], and [`getData`], you are able to create, delete, update, and read entities for your tests. The framework enables you to send HTTP requests with these statically defined data entities: @@ -183,8 +181,10 @@ The following is encoded in `<operation>`: The parameter that declares a body of the request is _catalogCategoryRepositoryV1SavePostBody_. Using the [Reference], we can trace how the JSON request was converted into XML representation. -Note: Comments in the example below are used to demonstrate relation between JSON request and MFTF metadata in XML. +<div class="bs-callout bs-callout-info"> +Comments in the example below are used to demonstrate relation between JSON request and MFTF metadata in XML. JSON does not support comments. +</div> Model schema for _catalogCategoryRepositoryV1SavePostBody_ with XML representation of _Catalog/Metadata/category-meta.xml_ in comments: diff --git a/docs/page.md b/docs/page.md index b0542aab0..52107e525 100644 --- a/docs/page.md +++ b/docs/page.md @@ -1,7 +1,5 @@ # Page structure -<span style="text-align: right">_This topic was updated due to the 2.3.13 MFTF release._</span> - The MFTF uses a modified concept of [PageObjects], which models the testing areas of your page as objects within the code. This reduces occurrences of duplicated code and enables you to fix things quickly, in one place, when things change. You define the contents of a page, for reference in a [`<test>`], at both the [`<page>`] and [`<section>`] level. diff --git a/docs/reporting.md b/docs/reporting.md index a6a69e45e..04b911778 100644 --- a/docs/reporting.md +++ b/docs/reporting.md @@ -299,8 +299,10 @@ To launch the generated report in a web browser: allure open dev/tests/acceptance/tests/_output/allure-report ``` -Note: By default, Allure generates reports in the `allure-report/` at the current directory. +<div class="bs-callout bs-callout-info"> +By default, Allure generates reports in the `allure-report/` at the current directory. For example, if you run the command without `-o` flag while you are in the `magento2/` directory, Allure will generate a report at the `magento2/allure-report/` directory. +</div> ```bash allure generate dev/tests/acceptance/tests/_output/allure-result diff --git a/docs/section.md b/docs/section.md index cf8668c32..5fbd06d3d 100644 --- a/docs/section.md +++ b/docs/section.md @@ -1,7 +1,5 @@ # Section structure -<span style="text-align: right">_This topic was updated due to the 2.3.13 MFTF release._</span> - A `<section>` is a reusable part of a [`<page>`](./page.md) and is the standard file for defining UI elements on a page used in a test. A `<section>` can define: diff --git a/docs/section/locator-functions.md b/docs/section/locator-functions.md index 40fa5a206..d52c2fc72 100644 --- a/docs/section/locator-functions.md +++ b/docs/section/locator-functions.md @@ -1,6 +1,5 @@ # Locator functions -<span style="text-align: right">_This topic was updated due to the 2.3.13 MFTF release._</span> ## Define Locator::functions in elements diff --git a/docs/section/parameterized-selectors.md b/docs/section/parameterized-selectors.md index adbe42773..19821b98d 100644 --- a/docs/section/parameterized-selectors.md +++ b/docs/section/parameterized-selectors.md @@ -1,6 +1,5 @@ # Parameterized selectors -<span style="text-align: right">_This topic was updated due to the 2.3.13 MFTF release._</span> Use the following examples to create and use parameterized selectors in the MFTF. diff --git a/docs/suite.md b/docs/suite.md index 768856901..c8b21afbf 100644 --- a/docs/suite.md +++ b/docs/suite.md @@ -1,7 +1,5 @@ # Suites -<span style="text-align: right">_This topic was updated due to the 2.3.13 MFTF release._</span> - Suites are essentially groups of tests that run in the specific conditions (preconditions and postconditions). They enable you including, excluding, and grouping tests for a customized test run when you need it. You can form suites using separate tests, groups, and modules. @@ -10,7 +8,9 @@ Each suite must be defined in the `<magento 2 root>/dev/tests/acceptance/tests/_ The generated tests for each suite go into a separate directory under `<magento 2 root>/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/_generated/`. By default, all generated tests are stored in the _default_ suite under `.../Magento/FunctionalTest/_generated/default/` -Note: If a test is generated into at least one custom suite, it will not appear in the _default_ suite. +<div class="bs-callout bs-callout-info"> + If a test is generated into at least one custom suite, it will not appear in the _default_ suite. +</div> ## Format @@ -216,8 +216,10 @@ A suite hook with preconditions that executes once before the suite tests. It may contain test steps with any [actions] and [action groups]. -Note: Tests in the suite are not run and screenshots are not saved in case of a failure in the before hook. +<div class="bs-callout bs-callout-info"> +Tests in the suite are not run and screenshots are not saved in case of a failure in the before hook. To troubleshoot the failure, run the suite locally. +</div> ### after {#after-tag} diff --git a/docs/test.md b/docs/test.md index c8e33c670..7f92c18ae 100644 --- a/docs/test.md +++ b/docs/test.md @@ -1,16 +1,16 @@ # Test -<span style="text-align: right">_This topic was updated due to the 2.3.13 MFTF release._</span> - Test cases in the Magento Functional Testing Framework (MFTF) are defined in XML as [`<tests>`]. `<tests>` is a [Codeception test container][Codeception] that contains multiple individual tests with test metadata and before and after actions. MFTF `<tests>` is considered a sequence of actions with associated parameters. Any failed [assertion] within a test constitutes a failed test. -Note: `<before>` and `<after>` hooks are not global within `<tests>`. +<div class="bs-callout bs-callout-info"> + `<before>` and `<after>` hooks are not global within `<tests>`. They only apply to the `<test>` in which they are declared. The steps in `<after>` are run in both successful **and** failed test runs. +</div> The following diagram shows the structure of an MFTF test case: diff --git a/docs/test/actions.md b/docs/test/actions.md index d6315dbce..7e4634668 100644 --- a/docs/test/actions.md +++ b/docs/test/actions.md @@ -1,6 +1,5 @@ # Test actions -<span style="text-align: right">_This topic was updated due to the 2.3.13 MFTF release._</span> Actions in the MFTF allow you to automate different scenarios of Magento user's actions. They are mostly XML implementations of [Codeception actions](http://codeception.com/docs/modules/WebDriver#Actions). diff --git a/docs/test/annotations.md b/docs/test/annotations.md index 93326ade4..f71044821 100644 --- a/docs/test/annotations.md +++ b/docs/test/annotations.md @@ -1,6 +1,5 @@ # Annotations -<span style="text-align: right">_This topic was updated due to the 2.3.13 MFTF release._</span> Annotations are essentially comments in the code. In PHP, they all are marked by a preceding `@` symbol. diff --git a/docs/test/assertions.md b/docs/test/assertions.md index 3944ba826..17a3d194a 100644 --- a/docs/test/assertions.md +++ b/docs/test/assertions.md @@ -1,6 +1,5 @@ # Assertions -<span style="text-align: right">_This topic was updated due to the 2.3.13 MFTF release._</span> Assertions serve to pass or fail the [test](../test.md#test-tag) if a condition is not met. These assertions will look familiar to you if you've used any other testing framework, like PHPUnit. diff --git a/docs/tips-tricks.md b/docs/tips-tricks.md index d0f31bf4a..85d55e196 100644 --- a/docs/tips-tricks.md +++ b/docs/tips-tricks.md @@ -1,7 +1,5 @@ # Tips and Tricks -<span style="text-align: right">_This topic was updated due to the 2.3.13 MFTF release._</span> - Sometimes, little changes can make a big difference in your project. Here are some test writing tips to keep everything running smoothly. ## Actions and action groups @@ -398,4 +396,5 @@ BAD: <click selector="{{ProductInWebsitesSection.website('Second Website')}}" stepKey="selectSecondWebsite"/> <click selector="{{ProductInWebsitesSection.website('Second Website')}}" stepKey="unselectSecondWebsite"/> ``` + <!--{% endraw %}--> diff --git a/docs/troubleshooting.md b/docs/troubleshooting.md index 3bc5df2cb..f2bf3472c 100644 --- a/docs/troubleshooting.md +++ b/docs/troubleshooting.md @@ -1,7 +1,5 @@ # Troubleshooting -<span style="text-align: right">_This topic was updated due to the 2.3.13 MFTF release._</span> - Having a little trouble with the MFTF? See some common errors and fixes below. ## WebDriver issues diff --git a/docs/update.md b/docs/update.md index 45723996c..a97580157 100644 --- a/docs/update.md +++ b/docs/update.md @@ -1,7 +1,5 @@ # Update the Magento Functional Testing Framework -<span style="text-align: right">_This topic was updated due to the 2.3.13 MFTF release._</span> - <div class="bs-callout bs-callout-info"> <a href="https://devdocs.magento.com/mftf/2.3/introduction.html#find-version">Find out your version</a> of the MFTF. diff --git a/docs/versioning.md b/docs/versioning.md index 08b2f3777..e0054ae75 100644 --- a/docs/versioning.md +++ b/docs/versioning.md @@ -1,4 +1,4 @@ -# Versioning +# Versioning This documemt describes the versioning policy for the Magento Functional Testing Framework (MFTF), including the version numbering schema. From 758de901b92f071edc0f88ccef7c8ad0a3a69fd3 Mon Sep 17 00:00:00 2001 From: Donald Booth <dobooth@adobe.com> Date: Thu, 7 Mar 2019 15:59:43 -0600 Subject: [PATCH 06/18] Fixed hrefs that pointed to mftf/2.3/ path. --- README.md | 27 +++++++++++++++++++-------- docs/getting-started.md | 2 +- docs/introduction.md | 2 +- docs/update.md | 2 +- 4 files changed, 22 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index f145817be..9939b298f 100755 --- a/README.md +++ b/README.md @@ -6,15 +6,15 @@ ## Installation -For the installation guidelines and system requirements, refer to [Getting Started](https://devdocs.magento.com/mftf/2.3/getting-started.html). +For the installation guidelines and system requirements, refer to [Getting Started][]. ## Contributing We would appreciate your contributions to new components or new features, changes to the existing features, tests, documentation, specifications, bug fixes, optimizations, or just good suggestions. Report about an issue or request features opening a GitHub issue. -Learn more about contributing in our [Contribution Guidelines](.github/CONTRIBUTING.md). +Learn more about contributing in our [Contribution Guidelines][]. -If you want to participate in the documentation work, see [DevDocs Contributing](https://github.com/magento/devdocs/blob/master/.github/CONTRIBUTING.md). +If you want to participate in the documentation work, see [DevDocs Contributing][]. ### Labels applied by the MFTF team @@ -55,15 +55,26 @@ These labels are applied by the MFTF development team to community contributed i ## Reporting security issues -To report security vulnerabilities and other security issues in the Magento software or web sites, send an email with the report at [security@magento.com](mailto:security@magento.com). +To report security vulnerabilities and other security issues in the Magento software or web sites, send an email with the report at [security@magento.com][]. Do not report security issues using GitHub. -Be sure to encrypt your e-mail with our [encryption key](https://info2.magento.com/rs/magentoenterprise/images/security_at_magento.asc) if it includes sensitive information. -Learn more about reporting security issues [here](https://magento.com/security/reporting-magento-security-issue). +Be sure to encrypt your e-mail with our [encryption key][] if it includes sensitive information. +Learn more about reporting security issues [here][]. -Stay up-to-date on the latest security news and patches for Magento by signing up for [Security Alert Notifications](https://magento.com/security/sign-up). +Stay up-to-date on the latest security news and patches for Magento by signing up for [Security Alert Notifications][]. ## License Each Magento source file included in this distribution is licensed under AGPL 3.0. -See the license [here](LICENSE_AGPL3.txt) or contact [license@magentocommerce.com](mailto:license@magentocommerce.com) for a copy. +See the license [here][] or contact [license@magentocommerce.com][] for a copy. + +<!-- Link Definitions --> +[Getting Started]: https://devdocs.magento.com/mftf/getting-started.html +[Contribution Guidelines]: .github/CONTRIBUTING.md +[DevDocs Contributing]: https://github.com/magento/devdocs/blob/master/.github/CONTRIBUTING.md +[security@magento.com]: mailto:security@magento.com +[encryption key]: https://info2.magento.com/rs/magentoenterprise/images/security_at_magento.asc +[here]: https://magento.com/security/reporting-magento-security-issue +[Security Alert Notifications]: https://magento.com/security/sign-up +[here]: LICENSE_AGPL3.txt +[license@magentocommerce.com]: mailto:license@magentocommerce.com \ No newline at end of file diff --git a/docs/getting-started.md b/docs/getting-started.md index 0e3bf05db..7cf377304 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -1,7 +1,7 @@ # Getting started <div class="bs-callout bs-callout-info"> -<a href="https://devdocs.magento.com/mftf/2.3/introduction.html#find-version">Find out your version</a> of the MFTF. +<a href="https://devdocs.magento.com/mftf/introduction.html#find-version">Find out your version</a> of the MFTF. The latest Magento 2.3 release supports MFTF 2.3.13. The latest Magento 2.2 release supports MFTF 2.3.8. diff --git a/docs/introduction.md b/docs/introduction.md index 74142d5bd..a7f3f98c8 100644 --- a/docs/introduction.md +++ b/docs/introduction.md @@ -1,6 +1,6 @@ # Introduction to the Magento Functional Testing Framework version 2.3 -<a href="https://devdocs.magento.com/mftf/2.3/introduction.html#find-version">Find out your version</a> of the MFTF. +<a href="https://devdocs.magento.com/mftf/introduction.html#find-version">Find out your version</a> of the MFTF. The latest Magento 2.3 release supports MFTF 2.3.13. The latest Magento 2.2 release supports MFTF 2.3.8. diff --git a/docs/update.md b/docs/update.md index a97580157..b779dc8a3 100644 --- a/docs/update.md +++ b/docs/update.md @@ -1,7 +1,7 @@ # Update the Magento Functional Testing Framework <div class="bs-callout bs-callout-info"> -<a href="https://devdocs.magento.com/mftf/2.3/introduction.html#find-version">Find out your version</a> of the MFTF. +<a href="https://devdocs.magento.com/mftf/introduction.html#find-version">Find out your version</a> of the MFTF. The latest Magento 2.3 release supports MFTF 2.3.13. The latest Magento 2.2 release supports MFTF 2.3.8. From f6efc8c011e6bd4db4a4021a20af17917db8ce66 Mon Sep 17 00:00:00 2001 From: Donald Booth <dobooth@adobe.com> Date: Thu, 7 Mar 2019 16:20:28 -0600 Subject: [PATCH 07/18] <span> to <div> since the latter is block element. --- docs/commands/codeception.md | 8 +++---- docs/commands/mftf.md | 2 -- docs/configuration.md | 8 +++---- docs/credentials.md | 4 ++-- docs/getting-started.md | 8 +++---- docs/introduction.md | 4 ++-- docs/reporting.md | 4 ++-- docs/section.md | 29 +++++++++++++++++-------- docs/section/parameterized-selectors.md | 4 ++-- docs/test/actions.md | 8 +++---- docs/troubleshooting.md | 9 ++++++-- 11 files changed, 51 insertions(+), 37 deletions(-) diff --git a/docs/commands/codeception.md b/docs/commands/codeception.md index 2c248d665..1d73f8eab 100644 --- a/docs/commands/codeception.md +++ b/docs/commands/codeception.md @@ -1,9 +1,9 @@ # CLI commands: vendor/bin/codecept -<span class="bs-callout bs-callout-warning"> +<div class="bs-callout bs-callout-warning"> We do not recommend using Codeception commands directly as they can break the MFTF basic workflow. -All the Codeception commands you need are wrapped using the [`mftf` tool][].</span> +All the Codeception commands you need are wrapped using the [`mftf` tool][].</div> To run the Codeception testing framework commands directly, change your directory to the `<Magento root>`. @@ -35,9 +35,9 @@ vendor/bin/codecept run functional --group example --skip-group skip vendor/bin/codecept run ``` -<span class="bs-callout .bs-callout-info" +<div class="bs-callout .bs-callout-info"> The following documentation corresponds to Codeception 2.3.8. -</span> +</div> ```bash Full reference: diff --git a/docs/commands/mftf.md b/docs/commands/mftf.md index 78ab22723..e418f7970 100644 --- a/docs/commands/mftf.md +++ b/docs/commands/mftf.md @@ -1,7 +1,5 @@ # CLI commands: vendor/bin/mftf -<span class="style="text-align: right"">_This topic was updated due to the 2.3.13 MFTF release._</span> - The Magento Functional Testing Framework (MFTF) introduces the command line interface (CLI) tool `vendor/bin/mftf` to facilitate your interaction with the framework. Note that `mftf` commands replace the `robo` commands that were used in previous releases. diff --git a/docs/configuration.md b/docs/configuration.md index 854a77bdf..0afd9cd22 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -18,9 +18,9 @@ Example: MAGENTO_BASE_URL=http://magento2.vagrant251 ``` -<span class=".bs-callout .bs-callout-info"> +<div class=".bs-callout .bs-callout-info"> If the `MAGENTO_BASE_URL` contains a subdirectory (like `http://magento.test/magento2ce`), specify [`MAGENTO_CLI_COMMAND_PATH`][]. -</span> +</div> ### MAGENTO_BACKEND_NAME @@ -86,10 +86,10 @@ SELENIUM_PROTOCOL=http SELENIUM_PATH=/wd/hub ``` -<span class=".bs-callout .bs-callout-warning"> +<div class=".bs-callout .bs-callout-warning"> `SELENIUM_*` values are required if you are running Selenium on an external system. If you change the configuration of the external Selenium server, you must update these values. -</span> +</div> #### SELENIUM_HOST diff --git a/docs/credentials.md b/docs/credentials.md index a8a4ab347..c325cf3c0 100644 --- a/docs/credentials.md +++ b/docs/credentials.md @@ -89,8 +89,8 @@ The MFTF dynamically retrieves, encrypts, and decrypts the sensitive data during Decrypted credentials do not appear in the console, error logs, or [test reports][]. The decrypted values are only available in the `.credentials` file. -<span class=".bs-callout .bs-callout-info"> -The MFTF tests delivered with Magento application do not use credentials and do not cover external services, because of sensitivity of the data.</span> +<div class=".bs-callout .bs-callout-info"> +The MFTF tests delivered with Magento application do not use credentials and do not cover external services, because of sensitivity of the data.</div> <!-- Link definitions --> [`fillField`]: test/actions.md#fillfield diff --git a/docs/getting-started.md b/docs/getting-started.md index 7cf377304..0b6362220 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -68,9 +68,9 @@ To disable the WYSIWYG and enable the web driver to process these fields as simp 3. In the WYSIWYG Options section set the **Enable WYSIWYG Editor** option to **Disabled Completely**. 4. Click **Save Config**. -<span class=".bs-callout .bs-callout-tip"> +<div class=".bs-callout .bs-callout-tip"> When you want to test the WYSIWYG functionality, re-enable WYSIWYG in your test suite. -</span> +</div> ### Security settings {#security-settings} @@ -115,10 +115,10 @@ vendor/bin/mftf generate:urn-catalog --force .idea/ See [`generate:urn-catalog`][] for more details.' -<span class=".bs-callout .bs-callout-tip"> +<div class=".bs-callout .bs-callout-tip"> You can simplify command entry by adding the absolute path to the `vendor/bin` directory path to your PATH environment variable. After adding the path, you can run `mftf` without having to include `vendor/bin`. -</span> +</div> ### Step 2. Edit environmental settings {#environment-settings} diff --git a/docs/introduction.md b/docs/introduction.md index a7f3f98c8..0e7357ff7 100644 --- a/docs/introduction.md +++ b/docs/introduction.md @@ -15,10 +15,10 @@ MFTF improves: - **Maintainability** based on simple test creation and overall structure. Because MFTF tests are written in XML, you no longer need to learn PHP to write tests. -<span class=".bs-callout .bs-callout-info"> +<div class=".bs-callout .bs-callout-info"> We are actively developing functional tests. Refer to `<magento_root>/app/code/<vendor_name>/<module_name>/Test/Mftf/` for examples. -</span> +</div> ## Audience diff --git a/docs/reporting.md b/docs/reporting.md index 04b911778..50ee4fce1 100644 --- a/docs/reporting.md +++ b/docs/reporting.md @@ -277,9 +277,9 @@ Each time you run tests, the MFTF appends an XML file with results at the `tests The official [Allure Test Report][] documentation is well-covered, so we'll list only the CLI commands that you would need for your day-to-day work. -<span class=".bs-callout .bs-callout-info"> +<div class=".bs-callout .bs-callout-info"> The following commands are relative to the Magento installation directory. -</span> +</div> To generate a report to the `allure-report/` at the current directory: diff --git a/docs/section.md b/docs/section.md index 5fbd06d3d..fd405a281 100644 --- a/docs/section.md +++ b/docs/section.md @@ -14,10 +14,10 @@ A `<section>` can define: Substitutable values in the test can be of the following formats: - String literals (`stringLiteral`) -- References to a [data entity](./data.md) (XML data from the corresponding `.../Data/*.xml`) such as `entityName.Field`. +- References to a [data entity][] (XML data from the corresponding `.../Data/*.xml`) such as `entityName.Field`. - Persisted data: - - `$persistedCreateDataKey.field$` for data created in the scope of a [test](./test.md#test-tag) using the [`<createData>`](./test/actions.md#createdata) action with `stepKey="persistedCreateDataKey"`. - - `$$persistedCreateDataKey.field$$` for data created in [before](./test.md#before-tag) and [after](./test.md#after-tag) hooks. Even though `<before>`and `<after>` are nested inside a [test](./test.md#test-tag), persisted data is stored differently when it is done in a test hook. Therefore it must be accessed with a different notation. + - `$persistedCreateDataKey.field$` for data created in the scope of a [test][] using the [`<createData>`][] action with `stepKey="persistedCreateDataKey"`. + - `$$persistedCreateDataKey.field$$` for data created in [before][] and [after][] hooks. Even though `<before>`and `<after>` are nested inside a [test][], persisted data is stored differently when it is done in a test hook. Therefore it must be accessed with a different notation. The following diagram shows the XML structure of an MFTF section: @@ -84,7 +84,7 @@ The following is an example of a call in test: ### section {#section-tag} -`<section>` contains the sequence of UI elements in a section of a [page](./page.md). +`<section>` contains the sequence of UI elements in a section of a [page][]. Attributes|Type|Use|Description ---|---|---|--- @@ -93,16 +93,16 @@ Attributes|Type|Use|Description ### element {#element-tag} -`<element>`is a UI element used in an [action](./test/actions.md). +`<element>`is a UI element used in an [action][]. Attributes|Type|Use|Description ---|---|---|--- `name`|string|required|The element name; Must be alphanumeric. `type`|string|required|The type of the element. Possible values: `text`, `textarea`, `input`, `button`, `checkbox`, `radio`, `checkboxset`, `radioset`, `date`, `file`, `select`, `multiselect`, `wysiwyg`, `iframe`, `block`. -`selector`|string|optional|[XPath](https://www.w3schools.com/xml/xpath_nodes.asp) or [CSS](https://www.w3schools.com/cssref/css_selectors.asp) selector of the element. -`locatorFunction`|string|optional|[Locator function](./section/locator-functions.md) declaration to be used in lieu of a selector. +`selector`|string|optional|[XPath][] or [CSS][] selector of the element. +`locatorFunction`|string|optional|[Locator function][] declaration to be used in lieu of a selector. `timeout`|string|optional|The timeout after interaction with the element (in seconds). The default is _none_. -`parameterized`|boolean|optional|Include and set to `true` if the `selector` for this element has parameters that need to be replaced for proper use. Learn more in [Parameterized selectors](./section/parameterized-selectors.md). +`parameterized`|boolean|optional|Include and set to `true` if the `selector` for this element has parameters that need to be replaced for proper use. Learn more in [Parameterized selectors][]. `remove`|boolean|optional|The default is `false`. Set to `true` to remove this element during parsing. #### `timeout` attribute {#timeout-attribute} @@ -138,4 +138,15 @@ Whenever the `signIn` button is used in a test, the MFTF will add a 30 second `w <!-- Link definitions --> -[waitForPageLoad]: test/actions.md#waitforpageload \ No newline at end of file +[waitForPageLoad]: test/actions.md#waitforpageload +[data entity]: ./data.md +[test]: ./test.md#test-tag +[`<createData>`]: ./test/actions.md#createdata +[before]: ./test.md#before-tag +[after]: ./test.md#after-tag +[page]: ./page.md +[action]: ./test/actions.md +[XPath]: https://www.w3schools.com/xml/xpath_nodes.asp +[CSS]: https://www.w3schools.com/cssref/css_selectors.asp +[Locator function]: ./section/locator-functions.md +[Parameterized selectors]: ./section/parameterized-selectors.md \ No newline at end of file diff --git a/docs/section/parameterized-selectors.md b/docs/section/parameterized-selectors.md index 19821b98d..40f015424 100644 --- a/docs/section/parameterized-selectors.md +++ b/docs/section/parameterized-selectors.md @@ -73,9 +73,9 @@ For the parameterized part of the selector, add `{{var1}}, {{var2}}, ..., {{varN </section> ``` -<span class="bs-callout .bs-callout-info"> +<div class="bs-callout .bs-callout-info"> There is no need to use sequential variables like `{{var1}}`, `{{var2}}`. Parameterized replacement reads variables and maps them to the test call of the element sequentially from left to right, meaning you can use a selector like `#element .{{categoryId}} .{{productId}}`." -</span> +</div> ## Use a parameterized selector in a test diff --git a/docs/test/actions.md b/docs/test/actions.md index 7e4634668..73a41badc 100644 --- a/docs/test/actions.md +++ b/docs/test/actions.md @@ -1349,8 +1349,8 @@ Note that the makeScreenshot action does not automatically add the screenshot to <makeScreenshot userInput="example" stepKey="screenshotPage"/> ``` -<span class=".bs-callout .bs-callout-info"> -This action does not add a screenshot to the Allure [report](../reporting.md).</span> +<div class=".bs-callout .bs-callout-info"> +This action does not add a screenshot to the Allure [report](../reporting.md).</div> ### maximizeWindow @@ -2474,9 +2474,9 @@ Attribute|Type|Use|Description Wait for all Magento loading overlays to disappear. -<span class=".bs-callout .bs-callout-info"> +<div class=".bs-callout .bs-callout-info"> The CSS class for loading masks is not used consistently throughout Magento. -Therefore, this convenience function tries to wait for various specific selectors.</span> +Therefore, this convenience function tries to wait for various specific selectors.</div> ```config # Wait for these classes to not be visible diff --git a/docs/troubleshooting.md b/docs/troubleshooting.md index f2bf3472c..dd392827e 100644 --- a/docs/troubleshooting.md +++ b/docs/troubleshooting.md @@ -21,7 +21,7 @@ Use of PhantomJS is not actually supported by the MFTF. #### Solution -For headless browsing, the [Headless Chrome](https://developers.google.com/web/updates/2017/04/headless-chrome){:target="_blank"} has better compatibility with the MFTF. +For headless browsing, the [Headless Chrome][]{:target="_blank"} has better compatibility with the MFTF. ### Chrome @@ -41,7 +41,7 @@ Chrome v62 is in the process of being rolled out, and it causes an error with Ch #### Solution -Use [ChromeDriver v2.33+](https://chromedriver.storage.googleapis.com/index.html?path=2.33/){:target="_blank"} and [Selenium Server Standalone v3.6.0+](http://www.seleniumhq.org/download/){:target="_blank"} in order to execute tests in Google Chrome v62+. +Use [ChromeDriver v2.33+][]{:target="_blank"} and [Selenium Server Standalone v3.6.0+][]{:target="_blank"} in order to execute tests in Google Chrome v62+. ### Firefox @@ -54,3 +54,8 @@ There's a compatibility issue with Codeception's `moveMouseOver` function and Ge #### Solution None yet. Solving this problem is dependent on a GeckoDriver fix. + +<!-- Link Definitions --> +[Headless Chrome]: https://developers.google.com/web/updates/2017/04/headless-chrome +[ChromeDriver v2.33+]: https://chromedriver.storage.googleapis.com/index.html?path=2.33/ +[Selenium Server Standalone v3.6.0+]: http://www.seleniumhq.org/download/ \ No newline at end of file From 5facbb366a79707a88448be0c5a70e17d741d889 Mon Sep 17 00:00:00 2001 From: Donald Booth <dobooth@adobe.com> Date: Thu, 7 Mar 2019 16:45:20 -0600 Subject: [PATCH 08/18] Close %raw% tag. --- docs/credentials.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/credentials.md b/docs/credentials.md index c325cf3c0..e8da8c849 100644 --- a/docs/credentials.md +++ b/docs/credentials.md @@ -80,7 +80,7 @@ For example: <fillField stepKey="FillApiToken" selector=".api-token" userInput="{{_CREDS.my_data_key}}" /> ``` -<!-- {% raw %} --> +<!-- {% endraw %} --> ## Implementation details From 70fe5a754c613c6612efcb3eaa8303c12a2cb917 Mon Sep 17 00:00:00 2001 From: Donald Booth <dobooth@adobe.com> Date: Thu, 7 Mar 2019 16:49:17 -0600 Subject: [PATCH 09/18] Dupe %raw% tag removed. --- docs/merging.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/merging.md b/docs/merging.md index 8762e6b58..32ea966c3 100644 --- a/docs/merging.md +++ b/docs/merging.md @@ -304,8 +304,6 @@ The controls change drastically in the B2B version, so it was abstracted to an a </actionGroup> ``` -<!-- {% raw %} --> - ## Merge pages Use [page] merging to add or remove [sections] in your module. @@ -560,6 +558,8 @@ The `_defaultSample` results corresponds to: </entity> ``` +<!-- {% endraw %} --> + <!-- Link definitions --> [`codecept`]: ./commands/codeception.md From b7dd56b5d28caaa1b5c156821954e45befc6e5bb Mon Sep 17 00:00:00 2001 From: Donald Booth <dobooth@adobe.com> Date: Fri, 8 Mar 2019 12:04:16 -0600 Subject: [PATCH 10/18] Fixing links and HTML. --- docs/commands/codeception.md | 8 ++++---- docs/configuration.md | 4 ++-- docs/credentials.md | 5 +++-- docs/extending.md | 3 ++- docs/getting-started.md | 19 ++++++++++--------- docs/introduction.md | 14 +++++++++----- docs/reporting.md | 4 ++-- docs/section/parameterized-selectors.md | 3 +-- docs/test.md | 2 +- docs/test/actions.md | 6 ++---- docs/test/annotations.md | 8 +++++--- docs/tips-tricks.md | 18 +++++++++--------- docs/update.md | 7 ++++--- docs/versioning.md | 3 +++ 14 files changed, 57 insertions(+), 47 deletions(-) diff --git a/docs/commands/codeception.md b/docs/commands/codeception.md index 1d73f8eab..0ea23f0e7 100644 --- a/docs/commands/codeception.md +++ b/docs/commands/codeception.md @@ -1,11 +1,11 @@ # CLI commands: vendor/bin/codecept - -<div class="bs-callout bs-callout-warning"> +<div class="bs-callout bs-callout-warning" markdown="1"> We do not recommend using Codeception commands directly as they can break the MFTF basic workflow. -All the Codeception commands you need are wrapped using the [`mftf` tool][].</div> +All the Codeception commands you need are wrapped using the [`mftf` tool][]. To run the Codeception testing framework commands directly, change your directory to the `<Magento root>`. +</div> ## Usage examples @@ -35,7 +35,7 @@ vendor/bin/codecept run functional --group example --skip-group skip vendor/bin/codecept run ``` -<div class="bs-callout .bs-callout-info"> +<div class="bs-callout bs-callout-info"> The following documentation corresponds to Codeception 2.3.8. </div> diff --git a/docs/configuration.md b/docs/configuration.md index 0afd9cd22..6b8e9ad51 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -18,7 +18,7 @@ Example: MAGENTO_BASE_URL=http://magento2.vagrant251 ``` -<div class=".bs-callout .bs-callout-info"> +<div class="bs-callout bs-callout-info" markdown="1"> If the `MAGENTO_BASE_URL` contains a subdirectory (like `http://magento.test/magento2ce`), specify [`MAGENTO_CLI_COMMAND_PATH`][]. </div> @@ -86,7 +86,7 @@ SELENIUM_PROTOCOL=http SELENIUM_PATH=/wd/hub ``` -<div class=".bs-callout .bs-callout-warning"> +<div class="bs-callout bs-callout-warning" markdown="1"> `SELENIUM_*` values are required if you are running Selenium on an external system. If you change the configuration of the external Selenium server, you must update these values. </div> diff --git a/docs/credentials.md b/docs/credentials.md index e8da8c849..7afa9b805 100644 --- a/docs/credentials.md +++ b/docs/credentials.md @@ -49,8 +49,9 @@ carriers_usps_password=Lmgxvrq89uPwECeV .... ``` -{: .bs-callout .bs-callout-info } +<div class="bs-callout bs-callout-info" markdown="1"> The `/` symbol is not supported in a key name. +</div> You are free to use any other keys you like, as they are merely the keys to reference from your tests. @@ -89,7 +90,7 @@ The MFTF dynamically retrieves, encrypts, and decrypts the sensitive data during Decrypted credentials do not appear in the console, error logs, or [test reports][]. The decrypted values are only available in the `.credentials` file. -<div class=".bs-callout .bs-callout-info"> +<div class="bs-callout bs-callout-info"> The MFTF tests delivered with Magento application do not use credentials and do not cover external services, because of sensitivity of the data.</div> <!-- Link definitions --> diff --git a/docs/extending.md b/docs/extending.md index b86d8b247..666121986 100644 --- a/docs/extending.md +++ b/docs/extending.md @@ -11,8 +11,9 @@ You can create or update any component of the parent body in your new test/actio Specify needed variations for a parent object and produce a copy of the original that incorporates the specified changes (the "delta"). +<div class="bs-callout bs-callout-info"> Unlike merging, the parent test (or action group) will still exist after the test generation. - {:.bs-callout .bs-callout-info} +</div> ## Extending tests diff --git a/docs/getting-started.md b/docs/getting-started.md index 0b6362220..5a64acc8a 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -1,8 +1,7 @@ # Getting started -<div class="bs-callout bs-callout-info"> -<a href="https://devdocs.magento.com/mftf/introduction.html#find-version">Find out your version</a> of the MFTF. - +<div class="bs-callout bs-callout-info" markdown="1"> +[Find your MFTF version][] of the MFTF. The latest Magento 2.3 release supports MFTF 2.3.13. The latest Magento 2.2 release supports MFTF 2.3.8. </div> @@ -16,9 +15,9 @@ Make sure that you have the following software installed and configured on your - [Java 1.8 or later][java] - [Selenium Server Standalone 3.6 or later][selenium server] and [ChromeDriver 2.33 or later][chrome driver] or other webdriver in the same directory -{:.bs-callout .bs-callout-tip} +<div class="bs-callout bs-callout-tip" markdown="1"> [PhpStorm] supports [Codeception test execution][], which is helpful when debugging. - +</div> ## Install Magento {#install-magento} Use instructions below to install Magento. @@ -68,7 +67,7 @@ To disable the WYSIWYG and enable the web driver to process these fields as simp 3. In the WYSIWYG Options section set the **Enable WYSIWYG Editor** option to **Disabled Completely**. 4. Click **Save Config**. -<div class=".bs-callout .bs-callout-tip"> +<div class="bs-callout bs-callout-tip"> When you want to test the WYSIWYG functionality, re-enable WYSIWYG in your test suite. </div> @@ -115,7 +114,7 @@ vendor/bin/mftf generate:urn-catalog --force .idea/ See [`generate:urn-catalog`][] for more details.' -<div class=".bs-callout .bs-callout-tip"> +<div class="bs-callout bs-callout-tip" markdown="1"> You can simplify command entry by adding the absolute path to the `vendor/bin` directory path to your PATH environment variable. After adding the path, you can run `mftf` without having to include `vendor/bin`. </div> @@ -142,8 +141,9 @@ Specify the following parameters, which are required to launch tests: - `MAGENTO_ADMIN_PASSWORD` must contain the user password required for authorization in the Admin area. Example: `MAGENTO_ADMIN_PASSWORD=123123q` -{: .bs-callout .bs-callout-info } +<div class="bs-callout bs-callout-info" markdown="1"> If the `MAGENTO_BASE_URL` contains a subdirectory like `http://magento.test/magento2ce`, specify `MAGENTO_CLI_COMMAND_PATH`. +</div> Learn more about environmental settings in [Configuration][]. @@ -300,8 +300,9 @@ allure serve dev/tests/_output/allure-results/ [install Allure]: https://github.com/allure-framework/allure2#download [java]: http://www.oracle.com/technetwork/java/javase/downloads/index.html [mftf tests]: introduction.md#mftf-tests -[php]: https://devdocs.magento.com/guides/2.3/install-gde/system-requirements-tech.md#php +[php]: https://devdocs.magento.com/guides/v2.3/install-gde/system-requirements-tech.md#php [PhpStorm]: https://www.jetbrains.com/phpstorm/ [selenium server]: https://www.seleniumhq.org/download/ [Set up a standalone MFTF]: #set-up-a-standalone-mftf [test suite]: suite.md +[Find your MFTF version]: introduction.md#find-your-mftf-version diff --git a/docs/introduction.md b/docs/introduction.md index 0e7357ff7..66e52bb76 100644 --- a/docs/introduction.md +++ b/docs/introduction.md @@ -1,6 +1,6 @@ -# Introduction to the Magento Functional Testing Framework version 2.3 +# Introduction to the Magento Functional Testing Framework -<a href="https://devdocs.magento.com/mftf/introduction.html#find-version">Find out your version</a> of the MFTF. +[Find your MFTF version][] of the MFTF. The latest Magento 2.3 release supports MFTF 2.3.13. The latest Magento 2.2 release supports MFTF 2.3.8. @@ -15,9 +15,11 @@ MFTF improves: - **Maintainability** based on simple test creation and overall structure. Because MFTF tests are written in XML, you no longer need to learn PHP to write tests. -<div class=".bs-callout .bs-callout-info"> + +<div class="bs-callout bs-callout-info" markdown="1"> We are actively developing functional tests. Refer to `<magento_root>/app/code/<vendor_name>/<module_name>/Test/Mftf/` for examples. +Check out the [MFTF Test Migration][] repo. </div> ## Audience @@ -46,7 +48,7 @@ As a Magento developer, test changes, such as extended search functionality, a n As a software engineer, perform regression testing before release to ensure that Magento works as expected with new functionality. -## Find your MFTF version {#find-version} +## Find your MFTF version There are two options to find out your MFTF version: @@ -132,4 +134,6 @@ Follow the [MFTF project] and [contribute on Github]. <!-- Link definitions --> [contribute on Github]: https://github.com/magento/magento2-functional-testing-framework/blob/master/.github/CONTRIBUTING.md [Functional Testing Framework]: https://devdocs.magento.com/guides/v2.3/mtf/mtf_introduction.html -[MFTF project]: https://github.com/magento/magento2-functional-testing-framework \ No newline at end of file +[MFTF project]: https://github.com/magento/magento2-functional-testing-framework +[Find your MFTF version]: #find-your-mftf-version +[MFTF Test Migration]: https://github.com/magento/magento-functional-tests-migration \ No newline at end of file diff --git a/docs/reporting.md b/docs/reporting.md index 50ee4fce1..f303945af 100644 --- a/docs/reporting.md +++ b/docs/reporting.md @@ -277,7 +277,7 @@ Each time you run tests, the MFTF appends an XML file with results at the `tests The official [Allure Test Report][] documentation is well-covered, so we'll list only the CLI commands that you would need for your day-to-day work. -<div class=".bs-callout .bs-callout-info"> +<div class="bs-callout bs-callout-info"> The following commands are relative to the Magento installation directory. </div> @@ -299,7 +299,7 @@ To launch the generated report in a web browser: allure open dev/tests/acceptance/tests/_output/allure-report ``` -<div class="bs-callout bs-callout-info"> +<div class="bs-callout bs-callout-info" markdown="1"> By default, Allure generates reports in the `allure-report/` at the current directory. For example, if you run the command without `-o` flag while you are in the `magento2/` directory, Allure will generate a report at the `magento2/allure-report/` directory. </div> diff --git a/docs/section/parameterized-selectors.md b/docs/section/parameterized-selectors.md index 40f015424..58227948f 100644 --- a/docs/section/parameterized-selectors.md +++ b/docs/section/parameterized-selectors.md @@ -1,6 +1,5 @@ # Parameterized selectors - Use the following examples to create and use parameterized selectors in the MFTF. ## Set up a selector in section @@ -73,7 +72,7 @@ For the parameterized part of the selector, add `{{var1}}, {{var2}}, ..., {{varN </section> ``` -<div class="bs-callout .bs-callout-info"> +<div class="bs-callout bs-callout-info" markdown="1"> There is no need to use sequential variables like `{{var1}}`, `{{var2}}`. Parameterized replacement reads variables and maps them to the test call of the element sequentially from left to right, meaning you can use a selector like `#element .{{categoryId}} .{{productId}}`." </div> diff --git a/docs/test.md b/docs/test.md index 7f92c18ae..462cd138e 100644 --- a/docs/test.md +++ b/docs/test.md @@ -6,7 +6,7 @@ Test cases in the Magento Functional Testing Framework (MFTF) are defined in XML MFTF `<tests>` is considered a sequence of actions with associated parameters. Any failed [assertion] within a test constitutes a failed test. -<div class="bs-callout bs-callout-info"> +<div class="bs-callout bs-callout-info" markdown="1"> `<before>` and `<after>` hooks are not global within `<tests>`. They only apply to the `<test>` in which they are declared. The steps in `<after>` are run in both successful **and** failed test runs. diff --git a/docs/test/actions.md b/docs/test/actions.md index 73a41badc..4a6c3f48e 100644 --- a/docs/test/actions.md +++ b/docs/test/actions.md @@ -1,6 +1,5 @@ # Test actions - Actions in the MFTF allow you to automate different scenarios of Magento user's actions. They are mostly XML implementations of [Codeception actions](http://codeception.com/docs/modules/WebDriver#Actions). Some actions drive browser elements, while others use REST APIs. @@ -153,7 +152,6 @@ The only difference is that different data is assigned to the attributes, which <!-- {% endraw %} --> Here, [`<click>`](#click) performs a click on a button that can be found by the selector that is stored in the `signInAccountButton` of the `StorefrontCustomerSignInFormSection`. -See the `StorefrontCustomerSignInPage.xml` file code in [step 2](#section-code). ## Actions returning a variable @@ -1349,7 +1347,7 @@ Note that the makeScreenshot action does not automatically add the screenshot to <makeScreenshot userInput="example" stepKey="screenshotPage"/> ``` -<div class=".bs-callout .bs-callout-info"> +<div class="bs-callout bs-callout-info"> This action does not add a screenshot to the Allure [report](../reporting.md).</div> ### maximizeWindow @@ -2474,7 +2472,7 @@ Attribute|Type|Use|Description Wait for all Magento loading overlays to disappear. -<div class=".bs-callout .bs-callout-info"> +<div class="bs-callout bs-callout-info"> The CSS class for loading masks is not used consistently throughout Magento. Therefore, this convenience function tries to wait for various specific selectors.</div> diff --git a/docs/test/annotations.md b/docs/test/annotations.md index f71044821..f30b8104e 100644 --- a/docs/test/annotations.md +++ b/docs/test/annotations.md @@ -77,11 +77,13 @@ The `<group>` element is an implementation of a [`@group`] Codeception tag. Any test can be a part of multiple groups. The purpose of grouping is to create a set of test for a functionality or purpose, such as all cart tests or all slow tests and run them together locally. -{:.bs-callout .bs-callout-warning} -Group values cannot collide with [suite] names. +<div class="bs-callout bs-callout-warning" markdown="1"> +Group values cannot collide with [suite][] names. +</div> -{:.bs-callout .bs-callout-tip} +<div class="bs-callout bs-callout-tip" markdown="1"> Add `<skip>` to the test to skip it during test run. +</div> Attribute|Type|Use|Definition ---|---|---|--- diff --git a/docs/tips-tricks.md b/docs/tips-tricks.md index 85d55e196..b4b0ba2b8 100644 --- a/docs/tips-tricks.md +++ b/docs/tips-tricks.md @@ -13,7 +13,7 @@ In the bad example we see two parameters being passed into the selector with lit **Why?** The next person maintaining the test or extending it may not be able to understand what the parameters are referencing. -<span stype="color:green"> +<span style="color:green"> Good </span> @@ -54,7 +54,7 @@ In the bad example, we perform some heavy UI steps first. **Why?** If something goes wrong there, then the critical `magentoCLI` commands may not get a chance to run, leaving Magento configured incorrectly for any upcoming tests. -<span stype="color:green"> +<span style="color:green"> Good: </span> @@ -108,7 +108,7 @@ And for `seeElement` it will output something like this: There is a subtle distinction: The first is a failure but it is the desired result: a 'positive failure'. The second is a proper result of the action. -<span stype="color:green"> +<span style="color:green"> Good: </span> @@ -128,7 +128,7 @@ Bad: Whenever possible, specify a `defaultValue` for action group arguments. -<span stype="color:green"> +<span style="color:green"> GOOD: </span> @@ -176,7 +176,7 @@ Build your tests using action groups, even if an action group contains a single Extending a single action group will update all tests that use this group. This improves maintainability as multiple instances of a failure can be fixed with a single action group update. -<span stype="color:green"> +<span style="color:green"> GOOD: </span> @@ -234,7 +234,7 @@ Do not use numbers to make a `stepKey` unique. **Why?** This helps with readability and clarity. -<span stype="color:green"> +<span style="color:green"> GOOD: </span> @@ -308,7 +308,7 @@ Example: </div> ``` -<span stype="color:green"> +<span style="color:green"> GOOD: </span> @@ -358,7 +358,7 @@ BAD: Use descriptive variable names to increase readability. **Why?** It makes the code easier to follow and update. - <span stype="color:green"> + <span style="color:green"> GOOD: </span> @@ -379,7 +379,7 @@ BAD: When working with input type `checkbox`, do not use the `click` action; use `checkOption` or `uncheckOption` instead. **Why?** A click does not make it clear what the ending state will be; it will simply toggle the current state. Using the proper actions will ensure the expected state of the checkbox. -<span stype="color:green"> +<span style="color:green"> GOOD: </span> diff --git a/docs/update.md b/docs/update.md index b779dc8a3..1a3137eb6 100644 --- a/docs/update.md +++ b/docs/update.md @@ -1,7 +1,7 @@ # Update the Magento Functional Testing Framework -<div class="bs-callout bs-callout-info"> -<a href="https://devdocs.magento.com/mftf/introduction.html#find-version">Find out your version</a> of the MFTF. +<div class="bs-callout bs-callout-info" markdown="1"> +[Find your version][] of the MFTF. The latest Magento 2.3 release supports MFTF 2.3.13. The latest Magento 2.2 release supports MFTF 2.3.8. @@ -76,4 +76,5 @@ To update the MFTF from the previous minor version: <!-- Link Definitions --> [Changelog]: https://github.com/magento/magento2-functional-testing-framework/blob/master/CHANGELOG.md [WYSIWYG settings]: getting-started.md#wysiwyg-settings -[Security settings]: getting-started.md#security-settings \ No newline at end of file +[Security settings]: getting-started.md#security-settings +[Find your version]: introduction.md#find-your-mftf-version \ No newline at end of file diff --git a/docs/versioning.md b/docs/versioning.md index e0054ae75..026cab8f8 100644 --- a/docs/versioning.md +++ b/docs/versioning.md @@ -24,12 +24,15 @@ MFTF versioning policy follows [Semantic Versioning](https://semver.org/) guidel Version numbering schemes help users to understand the scope of the changes made a new release. +```tree X.Y.Z | | | | | +-- Backward Compatible changes (Patch release - bug fixes, small additions) | +---- Backward Compatible changes (Minor release - small new features, bug fixes) +------ Backward Incompatible changes (Major release - new features and/or major changes) +``` + For example: - Magento 2 ships with MFTF version 2.3.9 From 5a9b5593ac89a92781288fe8e279f9ff483ac131 Mon Sep 17 00:00:00 2001 From: Donald Booth <dobooth@adobe.com> Date: Fri, 8 Mar 2019 13:47:01 -0600 Subject: [PATCH 11/18] Small fixes. --- docs/commands/codeception.md | 4 ++-- docs/debugging.md | 2 +- docs/getting-started.md | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/commands/codeception.md b/docs/commands/codeception.md index 0ea23f0e7..ff6ce27d6 100644 --- a/docs/commands/codeception.md +++ b/docs/commands/codeception.md @@ -2,7 +2,7 @@ <div class="bs-callout bs-callout-warning" markdown="1"> We do not recommend using Codeception commands directly as they can break the MFTF basic workflow. -All the Codeception commands you need are wrapped using the [`mftf` tool][]. +All the Codeception commands you need are wrapped using the [mftf tool][]. To run the Codeception testing framework commands directly, change your directory to the `<Magento root>`. </div> @@ -81,5 +81,5 @@ Options: <!-- Link definitions --> -[`mftf` tool]: mftf.md +[mftf tool]: mftf.md [annotation]: ../test/annotations.md \ No newline at end of file diff --git a/docs/debugging.md b/docs/debugging.md index f80ebaebb..4b839ff6e 100644 --- a/docs/debugging.md +++ b/docs/debugging.md @@ -34,4 +34,4 @@ Your Debug Configuration should now be able to run your test and pause execution <!-- Link definitions --> [Xdebug]: https://xdebug.org/docs/install -[PHPStorm]: https://github.com/SeleniumHQ/selenium/wiki/PageObjects +[PHPStorm]: https://www.jetbrains.com/phpstorm/ diff --git a/docs/getting-started.md b/docs/getting-started.md index 5a64acc8a..c81291352 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -300,7 +300,7 @@ allure serve dev/tests/_output/allure-results/ [install Allure]: https://github.com/allure-framework/allure2#download [java]: http://www.oracle.com/technetwork/java/javase/downloads/index.html [mftf tests]: introduction.md#mftf-tests -[php]: https://devdocs.magento.com/guides/v2.3/install-gde/system-requirements-tech.md#php +[php]: https://devdocs.magento.com/guides/v2.3/install-gde/system-requirements-tech.html#php [PhpStorm]: https://www.jetbrains.com/phpstorm/ [selenium server]: https://www.seleniumhq.org/download/ [Set up a standalone MFTF]: #set-up-a-standalone-mftf From c7e17b9db4fe2685925196075c192fe0b7f1624d Mon Sep 17 00:00:00 2001 From: Donald Booth <dobooth@adobe.com> Date: Tue, 12 Mar 2019 13:36:18 -0500 Subject: [PATCH 12/18] Adding CODEOWNERS file for git review control. --- CODEOWNERS | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 CODEOWNERS diff --git a/CODEOWNERS b/CODEOWNERS new file mode 100644 index 000000000..5ef399f40 --- /dev/null +++ b/CODEOWNERS @@ -0,0 +1,3 @@ +# CODEOWNERS file for /docs/ folder. +# Forces a review from other writers for anything within /docs/. +/docs/ @magento/devdocs-admins \ No newline at end of file From 537ffe8c8a6d0d25312714992bfbcc4d1772766c Mon Sep 17 00:00:00 2001 From: Donald Booth <dobooth@adobe.com> Date: Tue, 12 Mar 2019 13:55:21 -0500 Subject: [PATCH 13/18] Move to hidden folder. --- CODEOWNERS => .github/CODEOWNERS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename CODEOWNERS => .github/CODEOWNERS (76%) diff --git a/CODEOWNERS b/.github/CODEOWNERS similarity index 76% rename from CODEOWNERS rename to .github/CODEOWNERS index 5ef399f40..9aaee78fb 100644 --- a/CODEOWNERS +++ b/.github/CODEOWNERS @@ -1,3 +1,3 @@ # CODEOWNERS file for /docs/ folder. # Forces a review from other writers for anything within /docs/. -/docs/ @magento/devdocs-admins \ No newline at end of file +/docs/ @magento/devdocs-admins From 76a4c589d564d0a08d01a2d42c60abcce24fb885 Mon Sep 17 00:00:00 2001 From: Donald Booth <dobooth@adobe.com> Date: Mon, 18 Mar 2019 11:09:55 -0500 Subject: [PATCH 14/18] Add files. --- docs/merge_points/introduction.md | 21 +++++++ docs/merge_points/merge-action-groups.md | 72 ++++++++++++++++++++++++ 2 files changed, 93 insertions(+) create mode 100644 docs/merge_points/introduction.md create mode 100644 docs/merge_points/merge-action-groups.md diff --git a/docs/merge_points/introduction.md b/docs/merge_points/introduction.md new file mode 100644 index 000000000..b108ed908 --- /dev/null +++ b/docs/merge_points/introduction.md @@ -0,0 +1,21 @@ +# Merge Points for testing extensions in MFTF + +The Magento Functional Testing Framework (MFTF) allows great flexibility when writing XML tests for extensions. +All parts of tests can be used, reused, and merged to best suit your needs and cut down on needless duplication. + +Extension developers can utilitze these merge points to leverage existing tests and modify just the parts needed to test their extension. For instance, if your extension adds a form field to a Catalog admin page, you can modify the existing Catalog tests and add actions, data, etc as needed to test the custom field. +This topic shows how to merge and reuse test elements when testing extensions. + +Follow the links below for an example of how to merge: + +- Action Groups +- Data +- Pages +- Sections +- Tests + +and how to extend: + +- Action Groups +- Data +- Tests \ No newline at end of file diff --git a/docs/merge_points/merge-action-groups.md b/docs/merge_points/merge-action-groups.md new file mode 100644 index 000000000..385ba2bab --- /dev/null +++ b/docs/merge_points/merge-action-groups.md @@ -0,0 +1,72 @@ +# Merging Action Groups + +An action group is a set of individual actions working together as a group. +These action groups can be shared between tests and they also be modified to your needs. + +## Starting Test + +```xml +<actionGroup name="FillAdminSimpleProductForm"> + <arguments> + <argument name="category"/> + <argument name="simpleProduct"/> + </arguments> + <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="navigateToProductIndex"/> + <click selector="{{AdminProductGridActionSection.addProductToggle}}" stepKey="clickAddProductDropdown"/> + <click selector="{{AdminProductGridActionSection.addSimpleProduct}}" stepKey="clickAddSimpleProduct"/> + <fillField userInput="{{simpleProduct.name}}" selector="{{AdminProductFormSection.productName}}" stepKey="fillName"/> + <fillField userInput="{{simpleProduct.sku}}" selector="{{AdminProductFormSection.productSku}}" stepKey="fillSKU"/> + <fillField userInput="{{simpleProduct.price}}" selector="{{AdminProductFormSection.productPrice}}" stepKey="fillPrice"/> + <fillField userInput="{{simpleProduct.quantity}}" selector="{{AdminProductFormSection.productQuantity}}" stepKey="fillQuantity"/> + <searchAndMultiSelectOption selector="{{AdminProductFormSection.categoriesDropdown}}" parameterArray="[{{category.name}}]" stepKey="searchAndSelectCategory"/> + <click selector="{{AdminProductSEOSection.sectionHeader}}" stepKey="openSeoSection"/> + <fillField userInput="{{simpleProduct.urlKey}}" selector="{{AdminProductSEOSection.urlKeyInput}}" stepKey="fillUrlKey"/> + <click selector="{{AdminProductFormActionSection.saveButton}}" stepKey="saveProduct"/> + <seeElement selector="{{AdminProductMessagesSection.successMessage}}" stepKey="assertSaveMessageSuccess"/> + <seeInField userInput="{{simpleProduct.name}}" selector="{{AdminProductFormSection.productName}}" stepKey="assertFieldName"/> + <seeInField userInput="{{simpleProduct.sku}}" selector="{{AdminProductFormSection.productSku}}" stepKey="assertFieldSku"/> + <seeInField userInput="{{simpleProduct.price}}" selector="{{AdminProductFormSection.productPrice}}" stepKey="assertFieldPrice"/> + <click selector="{{AdminProductSEOSection.sectionHeader}}" stepKey="openSeoSectionAssert"/> + <seeInField userInput="{{simpleProduct.urlKey}}" selector="{{AdminProductSEOSection.urlKeyInput}}" stepKey="assertFieldUrlKey"/> +</actionGroup> +``` + +## File to merge + +```xml +<actionGroup name="FillAdminSimpleProductForm"> + <!-- This will be added after the step "fillQuantity" in the above test. --> + <click selector="{{MyExtensionSection.myCheckbox}}" stepKey="clickMyCheckbox" after="fillQuantity"/> +</actionGroup> +``` + +## Resultant file + +```xml +<actionGroup name="FillAdminSimpleProductForm"> + <arguments> + <argument name="category"/> + <argument name="simpleProduct"/> + </arguments> + <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="navigateToProductIndex"/> + <click selector="{{AdminProductGridActionSection.addProductToggle}}" stepKey="clickAddProductDropdown"/> + <click selector="{{AdminProductGridActionSection.addSimpleProduct}}" stepKey="clickAddSimpleProduct"/> + <fillField userInput="{{simpleProduct.name}}" selector="{{AdminProductFormSection.productName}}" stepKey="fillName"/> + <fillField userInput="{{simpleProduct.sku}}" selector="{{AdminProductFormSection.productSku}}" stepKey="fillSKU"/> + <fillField userInput="{{simpleProduct.price}}" selector="{{AdminProductFormSection.productPrice}}" stepKey="fillPrice"/> + <fillField userInput="{{simpleProduct.quantity}}" selector="{{AdminProductFormSection.productQuantity}}" stepKey="fillQuantity"/> + <!-- Merged line here --> + <click selector="{{MyExtensionSection.myCheckbox}}" stepKey="clickMyCheckbox"/> + + <searchAndMultiSelectOption selector="{{AdminProductFormSection.categoriesDropdown}}" parameterArray="[{{category.name}}]" stepKey="searchAndSelectCategory"/> + <click selector="{{AdminProductSEOSection.sectionHeader}}" stepKey="openSeoSection"/> + <fillField userInput="{{simpleProduct.urlKey}}" selector="{{AdminProductSEOSection.urlKeyInput}}" stepKey="fillUrlKey"/> + <click selector="{{AdminProductFormActionSection.saveButton}}" stepKey="saveProduct"/> + <seeElement selector="{{AdminProductMessagesSection.successMessage}}" stepKey="assertSaveMessageSuccess"/> + <seeInField userInput="{{simpleProduct.name}}" selector="{{AdminProductFormSection.productName}}" stepKey="assertFieldName"/> + <seeInField userInput="{{simpleProduct.sku}}" selector="{{AdminProductFormSection.productSku}}" stepKey="assertFieldSku"/> + <seeInField userInput="{{simpleProduct.price}}" selector="{{AdminProductFormSection.productPrice}}" stepKey="assertFieldPrice"/> + <click selector="{{AdminProductSEOSection.sectionHeader}}" stepKey="openSeoSectionAssert"/> + <seeInField userInput="{{simpleProduct.urlKey}}" selector="{{AdminProductSEOSection.urlKeyInput}}" stepKey="assertFieldUrlKey"/> +</actionGroup> +``` \ No newline at end of file From f901e4fc5eebb02451977e667ff5a25b61fece4f Mon Sep 17 00:00:00 2001 From: Donald Booth <dobooth@adobe.com> Date: Tue, 19 Mar 2019 14:40:11 -0500 Subject: [PATCH 15/18] Adding merge point docs. --- docs/merge_points/extend-action-groups.md | 98 +++++++++++++++ docs/merge_points/extend-data.md | 70 +++++++++++ docs/merge_points/extend-tests.md | 145 ++++++++++++++++++++++ docs/merge_points/introduction.md | 36 ++++-- docs/merge_points/merge-action-groups.md | 10 +- docs/merge_points/merge-data.md | 56 +++++++++ docs/merge_points/merge-pages.md | 50 ++++++++ docs/merge_points/merge-sections.md | 43 +++++++ docs/merge_points/merge-tests.md | 102 +++++++++++++++ 9 files changed, 597 insertions(+), 13 deletions(-) create mode 100644 docs/merge_points/extend-action-groups.md create mode 100644 docs/merge_points/extend-data.md create mode 100644 docs/merge_points/extend-tests.md create mode 100644 docs/merge_points/merge-data.md create mode 100644 docs/merge_points/merge-pages.md create mode 100644 docs/merge_points/merge-sections.md create mode 100644 docs/merge_points/merge-tests.md diff --git a/docs/merge_points/extend-action-groups.md b/docs/merge_points/extend-action-groups.md new file mode 100644 index 000000000..1f307e796 --- /dev/null +++ b/docs/merge_points/extend-action-groups.md @@ -0,0 +1,98 @@ +# Extend action groups + +Extending an action group doesn't affect the existing action group. + +In this example we add a `<click>` command to check the checkbox that our extension added with a new action group for the simple product creation form. + +## Starting action group + +```xml +<actionGroup name="FillAdminSimpleProductForm"> + <arguments> + <argument name="category"/> + <argument name="simpleProduct"/> + </arguments> + <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="navigateToProductIndex"/> + <click selector="{{AdminProductGridActionSection.addProductToggle}}" stepKey="clickAddProductDropdown"/> + <click selector="{{AdminProductGridActionSection.addSimpleProduct}}" stepKey="clickAddSimpleProduct"/> + <fillField userInput="{{simpleProduct.name}}" selector="{{AdminProductFormSection.productName}}" stepKey="fillName"/> + <fillField userInput="{{simpleProduct.sku}}" selector="{{AdminProductFormSection.productSku}}" stepKey="fillSKU"/> + <fillField userInput="{{simpleProduct.price}}" selector="{{AdminProductFormSection.productPrice}}" stepKey="fillPrice"/> + <fillField userInput="{{simpleProduct.quantity}}" selector="{{AdminProductFormSection.productQuantity}}" stepKey="fillQuantity"/> + <searchAndMultiSelectOption selector="{{AdminProductFormSection.categoriesDropdown}}" parameterArray="[{{category.name}}]" stepKey="searchAndSelectCategory"/> + <click selector="{{AdminProductSEOSection.sectionHeader}}" stepKey="openSeoSection"/> + <fillField userInput="{{simpleProduct.urlKey}}" selector="{{AdminProductSEOSection.urlKeyInput}}" stepKey="fillUrlKey"/> + <click selector="{{AdminProductFormActionSection.saveButton}}" stepKey="saveProduct"/> + <seeElement selector="{{AdminProductMessagesSection.successMessage}}" stepKey="assertSaveMessageSuccess"/> + <seeInField userInput="{{simpleProduct.name}}" selector="{{AdminProductFormSection.productName}}" stepKey="assertFieldName"/> + <seeInField userInput="{{simpleProduct.sku}}" selector="{{AdminProductFormSection.productSku}}" stepKey="assertFieldSku"/> + <seeInField userInput="{{simpleProduct.price}}" selector="{{AdminProductFormSection.productPrice}}" stepKey="assertFieldPrice"/> + <click selector="{{AdminProductSEOSection.sectionHeader}}" stepKey="openSeoSectionAssert"/> + <seeInField userInput="{{simpleProduct.urlKey}}" selector="{{AdminProductSEOSection.urlKeyInput}}" stepKey="assertFieldUrlKey"/> +</actionGroup> +``` + +## File to merge + +```xml +<actionGroup name="FillAdminSimpleProductFormWithMyExtension" extends="FillAdminSimpleProductForm"> + <!-- This will be added after the step "fillQuantity" on line 12 in the above test. --> + <click selector="{{MyExtensionSection.myCheckbox}}" stepKey="clickMyCheckbox" after="fillQuantity"/> +</actionGroup> +``` + +## Resultant action group + +Note that there are now two action groups below. + +```xml +<actionGroup name="FillAdminSimpleProductForm"> + <arguments> + <argument name="category"/> + <argument name="simpleProduct"/> + </arguments> + <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="navigateToProductIndex"/> + <click selector="{{AdminProductGridActionSection.addProductToggle}}" stepKey="clickAddProductDropdown"/> + <click selector="{{AdminProductGridActionSection.addSimpleProduct}}" stepKey="clickAddSimpleProduct"/> + <fillField userInput="{{simpleProduct.name}}" selector="{{AdminProductFormSection.productName}}" stepKey="fillName"/> + <fillField userInput="{{simpleProduct.sku}}" selector="{{AdminProductFormSection.productSku}}" stepKey="fillSKU"/> + <fillField userInput="{{simpleProduct.price}}" selector="{{AdminProductFormSection.productPrice}}" stepKey="fillPrice"/> + <fillField userInput="{{simpleProduct.quantity}}" selector="{{AdminProductFormSection.productQuantity}}" stepKey="fillQuantity"/> + <searchAndMultiSelectOption selector="{{AdminProductFormSection.categoriesDropdown}}" parameterArray="[{{category.name}}]" stepKey="searchAndSelectCategory"/> + <click selector="{{AdminProductSEOSection.sectionHeader}}" stepKey="openSeoSection"/> + <fillField userInput="{{simpleProduct.urlKey}}" selector="{{AdminProductSEOSection.urlKeyInput}}" stepKey="fillUrlKey"/> + <click selector="{{AdminProductFormActionSection.saveButton}}" stepKey="saveProduct"/> + <seeElement selector="{{AdminProductMessagesSection.successMessage}}" stepKey="assertSaveMessageSuccess"/> + <seeInField userInput="{{simpleProduct.name}}" selector="{{AdminProductFormSection.productName}}" stepKey="assertFieldName"/> + <seeInField userInput="{{simpleProduct.sku}}" selector="{{AdminProductFormSection.productSku}}" stepKey="assertFieldSku"/> + <seeInField userInput="{{simpleProduct.price}}" selector="{{AdminProductFormSection.productPrice}}" stepKey="assertFieldPrice"/> + <click selector="{{AdminProductSEOSection.sectionHeader}}" stepKey="openSeoSectionAssert"/> + <seeInField userInput="{{simpleProduct.urlKey}}" selector="{{AdminProductSEOSection.urlKeyInput}}" stepKey="assertFieldUrlKey"/> +</actionGroup> +<actionGroup name="FillAdminSimpleProductFormWithMyExtension"> + <arguments> + <argument name="category"/> + <argument name="simpleProduct"/> + </arguments> + <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="navigateToProductIndex"/> + <click selector="{{AdminProductGridActionSection.addProductToggle}}" stepKey="clickAddProductDropdown"/> + <click selector="{{AdminProductGridActionSection.addSimpleProduct}}" stepKey="clickAddSimpleProduct"/> + <fillField userInput="{{simpleProduct.name}}" selector="{{AdminProductFormSection.productName}}" stepKey="fillName"/> + <fillField userInput="{{simpleProduct.sku}}" 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="{{MyExtensionSection.myCheckbox}}" stepKey="clickMyCheckbox"/> + + <searchAndMultiSelectOption selector="{{AdminProductFormSection.categoriesDropdown}}" parameterArray="[{{category.name}}]" stepKey="searchAndSelectCategory"/> + <click selector="{{AdminProductSEOSection.sectionHeader}}" stepKey="openSeoSection"/> + <fillField userInput="{{simpleProduct.urlKey}}" selector="{{AdminProductSEOSection.urlKeyInput}}" stepKey="fillUrlKey"/> + <click selector="{{AdminProductFormActionSection.saveButton}}" stepKey="saveProduct"/> + <seeElement selector="{{AdminProductMessagesSection.successMessage}}" stepKey="assertSaveMessageSuccess"/> + <seeInField userInput="{{simpleProduct.name}}" selector="{{AdminProductFormSection.productName}}" stepKey="assertFieldName"/> + <seeInField userInput="{{simpleProduct.sku}}" selector="{{AdminProductFormSection.productSku}}" stepKey="assertFieldSku"/> + <seeInField userInput="{{simpleProduct.price}}" selector="{{AdminProductFormSection.productPrice}}" stepKey="assertFieldPrice"/> + <click selector="{{AdminProductSEOSection.sectionHeader}}" stepKey="openSeoSectionAssert"/> + <seeInField userInput="{{simpleProduct.urlKey}}" selector="{{AdminProductSEOSection.urlKeyInput}}" stepKey="assertFieldUrlKey"/> +</actionGroup> +``` \ No newline at end of file diff --git a/docs/merge_points/extend-data.md b/docs/merge_points/extend-data.md new file mode 100644 index 000000000..344d11935 --- /dev/null +++ b/docs/merge_points/extend-data.md @@ -0,0 +1,70 @@ +# Extend data entities + +Extending an action group doesn't affect the existing action group. + +In this example we add a `<click>` command to check the checkbox that our extension added with a new action group for the simple product creation form. + +## Starting entity + +```xml +<entity name="SimpleProduct" type="product"> + <data key="sku" unique="suffix">SimpleProduct</data> + <data key="type_id">simple</data> + <data key="attribute_set_id">4</data> + <data key="name" unique="suffix">SimpleProduct</data> + <data key="price">123.00</data> + <data key="visibility">4</data> + <data key="status">1</data> + <data key="quantity">1000</data> + <data key="urlKey" unique="suffix">simpleproduct</data> + <data key="weight">1</data> + <requiredEntity type="product_extension_attribute">EavStockItem</requiredEntity> + <requiredEntity type="custom_attribute_array">CustomAttributeCategoryIds</requiredEntity> +</entity> +``` + +## File to merge + +```xml +<entity name="ExtensionProduct" type="product" extends="SimpleProduct"> + <!-- myExtensionData will simply be added to the product and quantity will be changed to 1001. --> + <data key="quantity">1001</data> + <data key="myExtensionData">dataHere</data> +</entity> +``` + +## Resultant entity + +Note that there are now two data entities below. + +```xml +<entity name="SimpleProduct" type="product"> + <data key="sku" unique="suffix">SimpleProduct</data> + <data key="type_id">simple</data> + <data key="attribute_set_id">4</data> + <data key="name" unique="suffix">SimpleProduct</data> + <data key="price">123.00</data> + <data key="visibility">4</data> + <data key="status">1</data> + <data key="quantity">1000</data> + <data key="urlKey" unique="suffix">simpleproduct</data> + <data key="weight">1</data> + <requiredEntity type="product_extension_attribute">EavStockItem</requiredEntity> + <requiredEntity type="custom_attribute_array">CustomAttributeCategoryIds</requiredEntity> +</entity> +<entity name="ExtensionProduct" type="product"> + <data key="sku" unique="suffix">SimpleProduct</data> + <data key="type_id">simple</data> + <data key="attribute_set_id">4</data> + <data key="name" unique="suffix">SimpleProduct</data> + <data key="price">123.00</data> + <data key="visibility">4</data> + <data key="status">1</data> + <data key="quantity">1001</data> + <data key="urlKey" unique="suffix">simpleproduct</data> + <data key="weight">1</data> + <requiredEntity type="product_extension_attribute">EavStockItem</requiredEntity> + <requiredEntity type="custom_attribute_array">CustomAttributeCategoryIds</requiredEntity> + <data key="myExtensionData">dataHere</data> +</entity> +``` \ No newline at end of file diff --git a/docs/merge_points/extend-tests.md b/docs/merge_points/extend-tests.md new file mode 100644 index 000000000..7398e273d --- /dev/null +++ b/docs/merge_points/extend-tests.md @@ -0,0 +1,145 @@ +# Extend tests + +Data objects can be merged to cover the needs of your extension. + +In this example, we add an action group to a new copy of the original test for our extension. + +## Starting test + +```xml +<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="CRITICAL"/> + <testCaseId value="MAGETWO-23414"/> + <group value="product"/> + </annotations> + <before> + <createData entity="_defaultCategory" stepKey="createPreReqCategory"/> + </before> + <after> + <amOnPage url="admin/admin/auth/logout/" stepKey="amOnLogoutPage"/> + <deleteData createDataKey="createPreReqCategory" stepKey="deletePreReqCategory"/> + </after> + + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin1"/> + <actionGroup ref="FillAdminSimpleProductForm" stepKey="fillProductFieldsInAdmin"> + <argument name="category" value="$$createPreReqCategory$$"/> + <argument name="simpleProduct" value="_defaultProduct"/> + </actionGroup> + <actionGroup ref="AssertProductInStorefrontCategoryPage" stepKey="assertProductInStorefront1"> + <argument name="category" value="$$createPreReqCategory$$"/> + <argument name="product" value="_defaultProduct"/> + </actionGroup> + <actionGroup ref="AssertProductInStorefrontProductPage" stepKey="assertProductInStorefront2"> + <argument name="product" value="_defaultProduct"/> + </actionGroup> +</test> +``` + +## File to merge + +```xml +<test name="AdminCreateSimpleProductExtensionTest" extends="AdminCreateSimpleProductTest"> + <!-- Since this is its own test you need the annotations block --> + <annotations> + <features value="Catalog"/> + <stories value="Create a Simple Product via Admin"/> <!-- you should leave this the same since it is part of the same group --> + <title value="Admin should be able to create a Simple Product with my extension"/> + <description value="Admin should be able to create a Simple Product with my extension via the product grid"/> + <severity value="CRITICAL"/> + <testCaseId value="Extension/Github Issue Number"/> + <group value="product"/> + </annotations> + <!-- This will be added after the step "fillProductFieldsInAdmin" on line 20 in the above test. --> + <actionGroup ref="AddMyExtensionData" stepKey="extensionField" after="fillProductFieldsInAdmin"> + <argument name="extensionData" value="_myData"/> + </actionGroup> + + <!-- This will be added after the step "assertProductInStorefront2" on line 28 in the above test. --> + <actionGroup ref="AssertMyExtensionDataExists" stepKey="assertExtensionInformation" after="assertProductInStorefront2"> + <argument name="extensionData" value="_myData"/> + </actionGroup> +</test> +``` + +## Resultant test + +Note that there are now two tests below. + +```xml +<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="CRITICAL"/> + <testCaseId value="MAGETWO-23414"/> + <group value="product"/> + </annotations> + <before> + <createData entity="_defaultCategory" stepKey="createPreReqCategory"/> + </before> + <after> + <amOnPage url="admin/admin/auth/logout/" stepKey="amOnLogoutPage"/> + <deleteData createDataKey="createPreReqCategory" stepKey="deletePreReqCategory"/> + </after> + + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin1"/> + <actionGroup ref="FillAdminSimpleProductForm" stepKey="fillProductFieldsInAdmin"> + <argument name="category" value="$$createPreReqCategory$$"/> + <argument name="simpleProduct" value="_defaultProduct"/> + </actionGroup> + <actionGroup ref="AssertProductInStorefrontCategoryPage" stepKey="assertProductInStorefront1"> + <argument name="category" value="$$createPreReqCategory$$"/> + <argument name="product" value="_defaultProduct"/> + </actionGroup> + <actionGroup ref="AssertProductInStorefrontProductPage" stepKey="assertProductInStorefront2"> + <argument name="product" value="_defaultProduct"/> + </actionGroup> +</test> +<test name="AdminCreateSimpleProductExtensionTest"> + <annotations> + <features value="Catalog"/> + <stories value="Create a Simple Product via Admin"/> + <title value="Admin should be able to create a Simple Product with my extension"/> + <description value="Admin should be able to create a Simple Product with my extension via the product grid"/> + <severity value="CRITICAL"/> + <testCaseId value="Extension/Github Issue Number"/> + <group value="product"/> + </annotations> + <before> + <createData entity="_defaultCategory" stepKey="createPreReqCategory"/> + </before> + <after> + <amOnPage url="admin/admin/auth/logout/" stepKey="amOnLogoutPage"/> + <deleteData createDataKey="createPreReqCategory" stepKey="deletePreReqCategory"/> + </after> + + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin1"/> + <actionGroup ref="FillAdminSimpleProductForm" stepKey="fillProductFieldsInAdmin"> + <argument name="category" value="$$createPreReqCategory$$"/> + <argument name="simpleProduct" value="_defaultProduct"/> + </actionGroup> + + <actionGroup ref="AddMyExtensionData" stepKey="extensionField"> + <argument name="extensionData" value="_myData"/> + </actionGroup> + + <actionGroup ref="AssertProductInStorefrontCategoryPage" stepKey="assertProductInStorefront1"> + <argument name="category" value="$$createPreReqCategory$$"/> + <argument name="product" value="_defaultProduct"/> + </actionGroup> + <actionGroup ref="AssertProductInStorefrontProductPage" stepKey="assertProductInStorefront2"> + <argument name="product" value="_defaultProduct"/> + </actionGroup> + + <actionGroup ref="AssertMyExtensionDataExists" stepKey="assertExtensionInformation"> + <argument name="extensionData" value="_myData"/> + </actionGroup> +</test> +``` \ No newline at end of file diff --git a/docs/merge_points/introduction.md b/docs/merge_points/introduction.md index b108ed908..891f6e3fe 100644 --- a/docs/merge_points/introduction.md +++ b/docs/merge_points/introduction.md @@ -6,16 +6,34 @@ All parts of tests can be used, reused, and merged to best suit your needs and c Extension developers can utilitze these merge points to leverage existing tests and modify just the parts needed to test their extension. For instance, if your extension adds a form field to a Catalog admin page, you can modify the existing Catalog tests and add actions, data, etc as needed to test the custom field. This topic shows how to merge and reuse test elements when testing extensions. +## Merging + Follow the links below for an example of how to merge: -- Action Groups -- Data -- Pages -- Sections -- Tests +- [Merge Action Groups][] +- [Merge Data][] +- [Merge Pages][] +- [Merge Sections][] +- [Merge Tests][] + +## Extending + +Only Test, Action Group, and Data objects in the MFTF Framework can be extended. +Extending can be very useful for extension developers since it will not affect existing tests. + +Consult [when to use Extends][] to use extends when deciding whether to merge or extend. -and how to extend: +- [Extend Action Groups][] +- [Extend Data][] +- [Extend Tests][] -- Action Groups -- Data -- Tests \ No newline at end of file +<!-- Link definitions --> +[when to use Extends]: https://devdocs.magento.com/mftf/docs/best-practices.html#when-to-use-extends +[Merge Action Groups]: https://devdocs.magento.com/mftf/docs/merge_points/merge-action-groups.html +[Merge Data]: https://devdocs.magento.com/mftf/docs/merge_points/merge-data.html +[Merge Pages]: https://devdocs.magento.com/mftf/docs/merge_points/merge-pages.html +[Merge Sections]: https://devdocs.magento.com/mftf/docs/merge_points/merge-sections.html +[Merge Tests]: https://devdocs.magento.com/mftf/docs/merge_points/merge-tests.html +[Extend Action Groups]: https://devdocs.magento.com/mftf/docs/merge_points/extend-action-groups.html +[Extend Data]: https://devdocs.magento.com/mftf/docs/merge_points/extend-data.html +[Extend Tests]: https://devdocs.magento.com/mftf/docs/merge_points/extend-tests.html \ No newline at end of file diff --git a/docs/merge_points/merge-action-groups.md b/docs/merge_points/merge-action-groups.md index 385ba2bab..0485e7287 100644 --- a/docs/merge_points/merge-action-groups.md +++ b/docs/merge_points/merge-action-groups.md @@ -1,9 +1,11 @@ -# Merging Action Groups +# Merge action groups An action group is a set of individual actions working together as a group. These action groups can be shared between tests and they also be modified to your needs. -## Starting Test +In this example we add a `<click>` command to check the checkbox that our extension adds to the simple product creation form. + +## Starting test ```xml <actionGroup name="FillAdminSimpleProductForm"> @@ -40,7 +42,7 @@ These action groups can be shared between tests and they also be modified to you </actionGroup> ``` -## Resultant file +## Resultant test ```xml <actionGroup name="FillAdminSimpleProductForm"> @@ -57,7 +59,7 @@ These action groups can be shared between tests and they also be modified to you <fillField userInput="{{simpleProduct.quantity}}" selector="{{AdminProductFormSection.productQuantity}}" stepKey="fillQuantity"/> <!-- Merged line here --> <click selector="{{MyExtensionSection.myCheckbox}}" stepKey="clickMyCheckbox"/> - + <searchAndMultiSelectOption selector="{{AdminProductFormSection.categoriesDropdown}}" parameterArray="[{{category.name}}]" stepKey="searchAndSelectCategory"/> <click selector="{{AdminProductSEOSection.sectionHeader}}" stepKey="openSeoSection"/> <fillField userInput="{{simpleProduct.urlKey}}" selector="{{AdminProductSEOSection.urlKeyInput}}" stepKey="fillUrlKey"/> diff --git a/docs/merge_points/merge-data.md b/docs/merge_points/merge-data.md new file mode 100644 index 000000000..b3342cc46 --- /dev/null +++ b/docs/merge_points/merge-data.md @@ -0,0 +1,56 @@ +# Merge data + +Data objects can be merged to cover the needs of your extension. + +In this example we update the `quantity` to `1001` and add a new piece of data relevant to our extension. This will affect all other tests that use this data. + +## Starting entity + +```xml +<entity name="SimpleProduct" type="product"> + <data key="sku" unique="suffix">SimpleProduct</data> + <data key="type_id">simple</data> + <data key="attribute_set_id">4</data> + <data key="name" unique="suffix">SimpleProduct</data> + <data key="price">123.00</data> + <data key="visibility">4</data> + <data key="status">1</data> + <data key="quantity">1000</data> + <data key="urlKey" unique="suffix">simpleproduct</data> + <data key="weight">1</data> + <requiredEntity type="product_extension_attribute">EavStockItem</requiredEntity> + <requiredEntity type="custom_attribute_array">CustomAttributeCategoryIds</requiredEntity> +</entity> +``` + +## File to merge + +```xml +<entity name="SimpleProduct" type="product"> + <!-- myExtensionData will simply be added to the product and quantity will be changed to 1001. --> + <data key="quantity">1001</data> + <data key="myExtensionData">dataHere</data> +</entity> +``` + +## Resultant entity + +```xml +<entity name="SimpleProduct" type="product"> + <data key="sku" unique="suffix">SimpleProduct</data> + <data key="type_id">simple</data> + <data key="attribute_set_id">4</data> + <data key="name" unique="suffix">SimpleProduct</data> + <data key="price">123.00</data> + <data key="visibility">4</data> + <data key="status">1</data> + <!-- Quantity updated --> + <data key="quantity">1001</data> + <data key="urlKey" unique="suffix">simpleproduct</data> + <data key="weight">1</data> + <requiredEntity type="product_extension_attribute">EavStockItem</requiredEntity> + <requiredEntity type="custom_attribute_array">CustomAttributeCategoryIds</requiredEntity> + <!-- Data key merged --> + <data key="myExtensionData">dataHere</data> +</entity> +``` \ No newline at end of file diff --git a/docs/merge_points/merge-pages.md b/docs/merge_points/merge-pages.md new file mode 100644 index 000000000..c7a3e8fa8 --- /dev/null +++ b/docs/merge_points/merge-pages.md @@ -0,0 +1,50 @@ +# Merge pages + +Sections can be merged into pages to cover your extension. + +In this example we add a section that may be relevant to our extension to the list of sections underneath one page. + +## Starting page + +```xml +<page name="AdminCategoryPage" url="catalog/category/" area="admin" module="Magento_Catalog"> + <section name="AdminCategorySidebarActionSection"/> + <section name="AdminCategoryMainActionsSection"/> + <section name="AdminCategorySidebarTreeSection"/> + <section name="AdminCategoryBasicFieldSection"/> + <section name="AdminCategorySEOSection"/> + <section name="AdminCategoryProductsSection"/> + <section name="AdminCategoryProductsGridSection"/> + <section name="AdminCategoryModalSection"/> + <section name="AdminCategoryMessagesSection"/> + <section name="AdminCategoryContentSection"/> +</page> +``` + +## File to merge + +```xml +<page name="AdminCategoryPage" url="catalog/category/" area="admin" module="Magento_Catalog"> + <!-- myExtensionSection will simply be added to the page --> + <section name="MyExtensionSection"/> +</page> +``` + +## Resultant page + +```xml +<page name="AdminCategoryPage"> + <section name="AdminCategorySidebarActionSection"/> + <section name="AdminCategoryMainActionsSection"/> + <section name="AdminCategorySidebarTreeSection"/> + <section name="AdminCategoryBasicFieldSection"/> + <section name="AdminCategorySEOSection"/> + <section name="AdminCategoryProductsSection"/> + <section name="AdminCategoryProductsGridSection"/> + <section name="AdminCategoryModalSection"/> + <section name="AdminCategoryMessagesSection"/> + <section name="AdminCategoryContentSection"/> + <!-- New section merged --> + <section name="MyExtensionSection"/> +</page> +``` \ No newline at end of file diff --git a/docs/merge_points/merge-sections.md b/docs/merge_points/merge-sections.md new file mode 100644 index 000000000..e9f5acb55 --- /dev/null +++ b/docs/merge_points/merge-sections.md @@ -0,0 +1,43 @@ +# Merge sections + +Sections can be merged together to cover your extension. + +In this example we add another selector to the section on the products page section. + +## Starting section + +```xml +<section name="ProductsPageSection"> + <element name="addProductButton" type="button" selector="//button[@id='add_new_product-button']"/> + <element name="checkboxForProduct" type="button" selector="//*[contains(text(),'{{args}}')]/parent::td/preceding-sibling::td/label[@class='data-grid-checkbox-cell-inner']" parameterized="true"/> + <element name="actions" type="button" selector="//div[@class='col-xs-2']/div[@class='action-select-wrap']/button[@class='action-select']"/> + <element name="delete" type="button" selector="//*[contains(@class,'admin__data-grid-header-row row row-gutter')]//*[text()='Delete']"/> + <element name="ok" type="button" selector="//button[@data-role='action']//span[text()='OK']"/> + <element name="deletedSuccessMessage" type="button" selector="//*[@class='message message-success success']"/> +</section> +``` + +## File to merge + +```xml +<section name="ProductsPageSection"> + <!-- myExtensionElement will simply be added to the page --> + <element name="myExtensionElement" type="button" selector="input.myExtension"/> +</section> +``` + +## Resultant section + +```xml +<section name="ProductsPageSection"> + <element name="addProductButton" type="button" selector="//button[@id='add_new_product-button']"/> + <element name="checkboxForProduct" type="button" selector="//*[contains(text(),'{{args}}')]/parent::td/preceding-sibling::td/label[@class='data-grid-checkbox-cell-inner']" parameterized="true"/> + <element name="actions" type="button" selector="//div[@class='col-xs-2']/div[@class='action-select-wrap']/button[@class='action-select']"/> + <element name="delete" type="button" selector="//*[contains(@class,'admin__data-grid-header-row row row-gutter')]//*[text()='Delete']"/> + <element name="ok" type="button" selector="//button[@data-role='action']//span[text()='OK']"/> + <element name="deletedSuccessMessage" type="button" selector="//*[@class='message message-success success']"/> + <!-- New element merged --> + <element name="myExtensionElement" type="button" selector="input.myExtension"/> +</section> +</page> +``` \ No newline at end of file diff --git a/docs/merge_points/merge-tests.md b/docs/merge_points/merge-tests.md new file mode 100644 index 000000000..3958410e9 --- /dev/null +++ b/docs/merge_points/merge-tests.md @@ -0,0 +1,102 @@ +# Merge tests + +Tests can be merged to create a new test that covers new extension capabilities. + +In this example we add an action group that modifies the original test to interact with our extension sending in data we created. + +## Starting test + +```xml +<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="CRITICAL"/> + <testCaseId value="MAGETWO-23414"/> + <group value="product"/> + </annotations> + <before> + <createData entity="_defaultCategory" stepKey="createPreReqCategory"/> + </before> + <after> + <amOnPage url="admin/admin/auth/logout/" stepKey="amOnLogoutPage"/> + <deleteData createDataKey="createPreReqCategory" stepKey="deletePreReqCategory"/> + </after> + + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin1"/> + <actionGroup ref="FillAdminSimpleProductForm" stepKey="fillProductFieldsInAdmin"> + <argument name="category" value="$$createPreReqCategory$$"/> + <argument name="simpleProduct" value="_defaultProduct"/> + </actionGroup> + <actionGroup ref="AssertProductInStorefrontCategoryPage" stepKey="assertProductInStorefront1"> + <argument name="category" value="$$createPreReqCategory$$"/> + <argument name="product" value="_defaultProduct"/> + </actionGroup> + <actionGroup ref="AssertProductInStorefrontProductPage" stepKey="assertProductInStorefront2"> + <argument name="product" value="_defaultProduct"/> + </actionGroup> +</test> +``` + +## File to merge + +```xml +<test name="AdminCreateSimpleProductTest"> + <!-- This will be added after the step "fillProductFieldsInAdmin" in the above test. --> + <actionGroup ref="AddMyExtensionData" stepKey="extensionField" after="fillProductFieldsInAdmin"> + <argument name="extensionData" value="_myData"/> + </actionGroup> + + <!-- This will be added after the step "assertProductInStorefront2" in the above test. --> + <actionGroup ref="AssertMyExtensionDataExists" stepKey="assertExtensionInformation" after="assertProductInStorefront2"> + <argument name="extensionData" value="_myData"/> + </actionGroup> +</test> +``` + +## Resultant test + +```xml +<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="CRITICAL"/> + <testCaseId value="MAGETWO-23414"/> + <group value="product"/> + </annotations> + <before> + <createData entity="_defaultCategory" stepKey="createPreReqCategory"/> + </before> + <after> + <amOnPage url="admin/admin/auth/logout/" stepKey="amOnLogoutPage"/> + <deleteData createDataKey="createPreReqCategory" stepKey="deletePreReqCategory"/> + </after> + + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin1"/> + <actionGroup ref="FillAdminSimpleProductForm" stepKey="fillProductFieldsInAdmin"> + <argument name="category" value="$$createPreReqCategory$$"/> + <argument name="simpleProduct" value="_defaultProduct"/> + </actionGroup> + <!-- First merged action group --> + <actionGroup ref="AddMyExtensionData" stepKey="extensionField"> + <argument name="extensionData" value="_myData"/> + </actionGroup> + + <actionGroup ref="AssertProductInStorefrontCategoryPage" stepKey="assertProductInStorefront1"> + <argument name="category" value="$$createPreReqCategory$$"/> + <argument name="product" value="_defaultProduct"/> + </actionGroup> + <actionGroup ref="AssertProductInStorefrontProductPage" stepKey="assertProductInStorefront2"> + <argument name="product" value="_defaultProduct"/> + </actionGroup> + <!-- Second merged action group --> + <actionGroup ref="AssertMyExtensionDataExists" stepKey="assertExtensionInformation"> + <argument name="extensionData" value="_myData"/> + </actionGroup> +</test> +``` \ No newline at end of file From 551e9259a6b8bbd79244dfaf745cc84e1709314f Mon Sep 17 00:00:00 2001 From: Donald Booth <dobooth@adobe.com> Date: Wed, 20 Mar 2019 12:02:54 -0500 Subject: [PATCH 16/18] Switched .md links to .html --- .github/CONTRIBUTING.md | 2 +- README.md | 2 +- composer.lock | 2 +- docs/best-practices.md | 32 ++++++++++++------------- docs/commands/codeception.md | 4 ++-- docs/commands/mftf.md | 4 ++-- docs/configuration.md | 4 ++-- docs/credentials.md | 8 +++---- docs/data.md | 4 ++-- docs/extending.md | 8 +++---- docs/getting-started.md | 18 +++++++------- docs/merging.md | 14 +++++------ docs/metadata.md | 12 +++++----- docs/page.md | 8 +++---- docs/reporting.md | 10 ++++---- docs/section.md | 22 ++++++++--------- docs/section/locator-functions.md | 4 ++-- docs/section/parameterized-selectors.md | 2 +- docs/suite.md | 8 +++---- docs/test.md | 14 +++++------ docs/test/action-groups.md | 6 ++--- docs/test/actions.md | 16 ++++++------- docs/test/annotations.md | 4 ++-- docs/test/assertions.md | 6 ++--- docs/update.md | 6 ++--- 25 files changed, 110 insertions(+), 110 deletions(-) diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index ef21d7bde..61805aafe 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -160,7 +160,7 @@ Label| Description [create issue]: https://help.github.com/articles/creating-an-issue/ [create pr]: https://help.github.com/articles/creating-a-pull-request/ [Definition of Done]: https://devdocs.magento.com/guides/v2.2/contributor-guide/contributing_dod.html -[devdocs]: https://github.com/magento/devdocs/blob/master/.github/CONTRIBUTING.md +[devdocs]: https://github.com/magento/devdocs/blob/master/.github/CONTRIBUTING.html [existing issues]: https://github.com/magento/magento2-functional-testing-framework/issues?q=is%3Aopen+is%3Aissue [existing PRs]: https://github.com/magento/magento2-functional-testing-framework/pulls?q=is%3Aopen+is%3Apr [GitHub documentation]: https://help.github.com/articles/syncing-a-fork diff --git a/README.md b/README.md index 9939b298f..7c3d647a4 100755 --- a/README.md +++ b/README.md @@ -70,7 +70,7 @@ See the license [here][] or contact [license@magentocommerce.com][] for a copy. <!-- Link Definitions --> [Getting Started]: https://devdocs.magento.com/mftf/getting-started.html -[Contribution Guidelines]: .github/CONTRIBUTING.md +[Contribution Guidelines]: .github/CONTRIBUTING.html [DevDocs Contributing]: https://github.com/magento/devdocs/blob/master/.github/CONTRIBUTING.md [security@magento.com]: mailto:security@magento.com [encryption key]: https://info2.magento.com/rs/magentoenterprise/images/security_at_magento.asc diff --git a/composer.lock b/composer.lock index e55da9399..4e4f4b4e6 100644 --- a/composer.lock +++ b/composer.lock @@ -1,7 +1,7 @@ { "_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#installing-dependencies", + "Read more about it at https://getcomposer.org/doc/01-basic-usage.html#installing-dependencies", "This file is @generated automatically" ], "content-hash": "e77971c8706a56d00fba57995a9b747d", diff --git a/docs/best-practices.md b/docs/best-practices.md index e3800b8ea..180f815ca 100644 --- a/docs/best-practices.md +++ b/docs/best-practices.md @@ -148,19 +148,19 @@ Since the configurable product module could be disabled, this approach is more r <!-- Link definitions --> -[`<after>`]: test/actions.md#before-and-after -[`<before>`]: test/actions.md#before-and-after -[`<comment>`]: test/actions.md#comment -[`<createData>`]: test/actions.md#createdata -[`<deleteData>`]: test/actions.md#deletedata -[`<wait>`]: test/actions.md#wait -[`<waitForElement>`]: test/actions.md#waitforelement -[`<waitForElementVisible>`]: test/actions.md#waitforelementvisible -[`<waitForLoadingMaskToDisappear>`]: test/actions.md#waitforloadingmasktodisappear -[Action group]: test/action-groups.md -[annotations]: test/annotations.md -[entity]: data.md -[extension]: extending.md -[merging]: merging.md -[parameterized selectors]: section/parameterized-selectors.md -[sections]: section.md +[`<after>`]: test/actions.html#before-and-after +[`<before>`]: test/actions.html#before-and-after +[`<comment>`]: test/actions.html#comment +[`<createData>`]: test/actions.html#createdata +[`<deleteData>`]: test/actions.html#deletedata +[`<wait>`]: test/actions.html#wait +[`<waitForElement>`]: test/actions.html#waitforelement +[`<waitForElementVisible>`]: test/actions.html#waitforelementvisible +[`<waitForLoadingMaskToDisappear>`]: test/actions.html#waitforloadingmasktodisappear +[Action group]: test/action-groups.html +[annotations]: test/annotations.html +[entity]: data.html +[extension]: extending.html +[merging]: merging.html +[parameterized selectors]: section/parameterized-selectors.html +[sections]: section.html diff --git a/docs/commands/codeception.md b/docs/commands/codeception.md index ff6ce27d6..d3b52216f 100644 --- a/docs/commands/codeception.md +++ b/docs/commands/codeception.md @@ -81,5 +81,5 @@ Options: <!-- Link definitions --> -[mftf tool]: mftf.md -[annotation]: ../test/annotations.md \ No newline at end of file +[mftf tool]: mftf.html +[annotation]: ../test/annotations.html \ No newline at end of file diff --git a/docs/commands/mftf.md b/docs/commands/mftf.md index e418f7970..0516f2d0a 100644 --- a/docs/commands/mftf.md +++ b/docs/commands/mftf.md @@ -384,11 +384,11 @@ vendor/bin/mftf upgrade:tests /Users/user/magento2/app/code/Magento/Catalog/Test <!-- LINK DEFINITIONS --> -[configuration]: ../configuration.md +[configuration]: ../configuration.html [Reference]: #reference [build]: #buildproject [setup]: #setupenv -[Reporting]: ../reporting.md +[Reporting]: ../reporting.html <!-- Abbreviations --> diff --git a/docs/configuration.md b/docs/configuration.md index 6b8e9ad51..6e5a519d1 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -254,6 +254,6 @@ BROWSER=firefox <!-- Link definitions --> [`MAGENTO_CLI_COMMAND_PATH`]: #magento_cli_command_path -[generateDate]: test/actions.md#generatedate -[mftf]: commands/mftf.md +[generateDate]: test/actions.html#generatedate +[mftf]: commands/mftf.html [timezones]: http://php.net/manual/en/timezones.php \ No newline at end of file diff --git a/docs/credentials.md b/docs/credentials.md index 7afa9b805..8939e0b4e 100644 --- a/docs/credentials.md +++ b/docs/credentials.md @@ -94,7 +94,7 @@ The decrypted values are only available in the `.credentials` file. The MFTF tests delivered with Magento application do not use credentials and do not cover external services, because of sensitivity of the data.</div> <!-- Link definitions --> -[`fillField`]: test/actions.md#fillfield -[data]: data.md -[initial setup]: getting-started.md -[test reports]: reporting.md +[`fillField`]: test/actions.html#fillfield +[data]: data.html +[initial setup]: getting-started.html +[test reports]: reporting.html diff --git a/docs/data.md b/docs/data.md index c5845c1d4..cc8c32c26 100644 --- a/docs/data.md +++ b/docs/data.md @@ -74,7 +74,7 @@ This emphasizes the practice for the `stepKey` of `createData` to be descriptive ## Use data returned by test actions -A test can also reference data that was returned as a result of [test actions](./test/actions.md#actions-returning-a-variable), like the action `<grabValueFrom selector="someSelector" stepKey="grabStepKey>`. +A test can also reference data that was returned as a result of [test actions](./test/actions.html#actions-returning-a-variable), like the action `<grabValueFrom selector="someSelector" stepKey="grabStepKey>`. Further in the test, the data grabbed by the `someSelector` selector can be referenced using the `stepKey` value. In this case, it is `grabStepKey`. @@ -89,7 +89,7 @@ The following example shows the usage of `grabValueFrom` in testing, where the r The data to operate against can be included as literals in a test. Hard-coded data input can be useful in assertions. -See also [Actions](./test/actions.md). +See also [Actions](./test/actions.html). ```xml userInput="We'll email you an order confirmation with details and tracking info." diff --git a/docs/extending.md b/docs/extending.md index 666121986..4c8371e49 100644 --- a/docs/extending.md +++ b/docs/extending.md @@ -363,7 +363,7 @@ __Use case__: Create an entity named `DivPanelGreen`, which is similar to the `D ``` <!-- Link definitions --> -[test]: ./test.md -[data]: ./data.md -[action group]: ./test/action-groups.md -[actions]: ./test/actions.md \ No newline at end of file +[test]: ./test.html +[data]: ./data.html +[action group]: ./test/action-groups.html +[actions]: ./test/actions.html \ No newline at end of file diff --git a/docs/getting-started.md b/docs/getting-started.md index c81291352..395e1824c 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -284,25 +284,25 @@ allure serve dev/tests/_output/allure-results/ <!-- Link definitions --> -[`codecept`]: commands/codeception.md -[`generate:urn-catalog`]: commands/mftf.md#generateurn-catalog -[`MAGENTO_BP`]: configuration.md#magento_bp -[`mftf`]: commands/mftf.md +[`codecept`]: commands/codeception.html +[`generate:urn-catalog`]: commands/mftf.html#generateurn-catalog +[`MAGENTO_BP`]: configuration.html#magento_bp +[`mftf`]: commands/mftf.html [allure docs]: https://docs.qameta.io/allure/ [Allure Framework]: http://allure.qatools.ru/ -[basic configuration]: configuration.md#basic-configuration +[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/ -[Configuration]: configuration.md +[Configuration]: configuration.html [contributing]: https://github.com/magento/magento2-functional-testing-framework/blob/develop/.github/CONTRIBUTING.md [install Allure]: https://github.com/allure-framework/allure2#download [java]: http://www.oracle.com/technetwork/java/javase/downloads/index.html -[mftf tests]: introduction.md#mftf-tests +[mftf tests]: introduction.html#mftf-tests [php]: https://devdocs.magento.com/guides/v2.3/install-gde/system-requirements-tech.html#php [PhpStorm]: https://www.jetbrains.com/phpstorm/ [selenium server]: https://www.seleniumhq.org/download/ [Set up a standalone MFTF]: #set-up-a-standalone-mftf -[test suite]: suite.md -[Find your MFTF version]: introduction.md#find-your-mftf-version +[test suite]: suite.html +[Find your MFTF version]: introduction.html#find-your-mftf-version diff --git a/docs/merging.md b/docs/merging.md index 32ea966c3..8a892b74a 100644 --- a/docs/merging.md +++ b/docs/merging.md @@ -562,10 +562,10 @@ The `_defaultSample` results corresponds to: <!-- Link definitions --> -[`codecept`]: ./commands/codeception.md -[`mftf`]: ./commands/mftf.md -[`<data>`]: ./data.md -[elements]: ./section.md#element-tag -[`<pages>`]: ./page.md -[`<sections>`]: ./section.md -[`<tests>`]: ./test.md +[`codecept`]: ./commands/codeception.html +[`mftf`]: ./commands/mftf.html +[`<data>`]: ./data.html +[elements]: ./section.html#element-tag +[`<pages>`]: ./page.html +[`<sections>`]: ./section.html +[`<tests>`]: ./test.html diff --git a/docs/metadata.md b/docs/metadata.md index 178871ea1..edec18fcb 100644 --- a/docs/metadata.md +++ b/docs/metadata.md @@ -554,7 +554,7 @@ Example: <!-- LINK DEFINITIONS --> -[actions]: test/actions.md +[actions]: test/actions.html [api reference]: https://devdocs.magento.com/guides/v2.3/get-started/bk-get-started-api.html [application/x-www-form-urlencoded]: https://www.w3.org/TR/html401/interact/forms.html#h-17.13.4.1 {:target="_blank"} @@ -562,12 +562,12 @@ Example: [catalogCategoryRepositoryV1SavePostBody]: #catalogCategoryRepositoryV1SavePostBody [contentType]: #contentType-tag [Create a new category]: #create-object-as-adminOauth -[createData]: test/actions.md#createdata +[createData]: test/actions.html#createdata [delete a category by its ID]: #delete-object-as-adminOauth -[deleteData]: test/actions.md#deletedata -[entity]: data.md#entity-tag +[deleteData]: test/actions.html#deletedata +[entity]: data.html#entity-tag [get information about a category by its ID]: #get-object-as-adminOauth -[getData]: test/actions.md#getdata +[getData]: test/actions.html#getdata [HTML forms]: https://www.w3.org/TR/html401/interact/forms.html {:target="_blank"} [oauth]: https://devdocs.magento.com/guides/v2.3/get-started/authentication/gs-authentication-oauth.html @@ -577,7 +577,7 @@ Example: [rest request]: #handling-with-api [html form]: #using-html-forms [update category data by its ID]: #update-object-as-adminOauth -[updateData]: test/actions.md#updatedata +[updateData]: test/actions.html#updatedata [rest response]: #rest-response *[CRUD]: Create Read Update Delete diff --git a/docs/page.md b/docs/page.md index 52107e525..aef0cdd76 100644 --- a/docs/page.md +++ b/docs/page.md @@ -161,12 +161,12 @@ Attributes|Type|Use|Description `remove`|boolean|optional|The default value is `"false"`. Set to `"true"` to remove this element during parsing. <!-- Link definitions --> -[`<createData>`]: test/actions.md#createdata +[`<createData>`]: test/actions.html#createdata [`<page>`]: #page-tag [`<section>`]: #section-tag -[`<test>`]: test.md -[actions]: test/actions.md +[`<test>`]: test.html +[actions]: test/actions.html [explicit page]: #explicit-page [PageObjects]: https://github.com/SeleniumHQ/selenium/wiki/PageObjects [parameterized page]: #parameterized-page -[section]: section.md +[section]: section.html diff --git a/docs/reporting.md b/docs/reporting.md index f303945af..dd6fed8ee 100644 --- a/docs/reporting.md +++ b/docs/reporting.md @@ -344,13 +344,13 @@ Refer to the [Reporting section][] for more Allure CLI details. <!-- Link definitions --> -[`after`]: test.md#after-tag -[`run:group`]: commands/mftf.md#rungroup -[`run:test`]: commands/mftf.md#runtest +[`after`]: test.html#after-tag +[`run:group`]: commands/mftf.html#rungroup +[`run:test`]: commands/mftf.html#runtest [Allure Framework]: https://docs.qameta.io/allure/ [Allure Test Report]: http://allure.qatools.ru/ -[codecept]: commands/codeception.md +[codecept]: commands/codeception.html [codeception]: https://codeception.com/docs/reference/Commands -[mftf]: commands/mftf.md +[mftf]: commands/mftf.html [report an issue]: https://github.com/magento/magento2-functional-testing-framework/blob/master/.github/CONTRIBUTING.md#report-an-issue [Reporting section]: https://docs.qameta.io/allure/#_reporting diff --git a/docs/section.md b/docs/section.md index fd405a281..745d2e574 100644 --- a/docs/section.md +++ b/docs/section.md @@ -1,6 +1,6 @@ # Section structure -A `<section>` is a reusable part of a [`<page>`](./page.md) and is the standard file for defining UI elements on a page used in a test. +A `<section>` is a reusable part of a [`<page>`](./page.html) and is the standard file for defining UI elements on a page used in a test. A `<section>` can define: @@ -138,15 +138,15 @@ Whenever the `signIn` button is used in a test, the MFTF will add a 30 second `w <!-- Link definitions --> -[waitForPageLoad]: test/actions.md#waitforpageload -[data entity]: ./data.md -[test]: ./test.md#test-tag -[`<createData>`]: ./test/actions.md#createdata -[before]: ./test.md#before-tag -[after]: ./test.md#after-tag -[page]: ./page.md -[action]: ./test/actions.md +[waitForPageLoad]: test/actions.html#waitforpageload +[data entity]: ./data.html +[test]: ./test.html#test-tag +[`<createData>`]: ./test/actions.html#createdata +[before]: ./test.html#before-tag +[after]: ./test.html#after-tag +[page]: ./page.html +[action]: ./test/actions.html [XPath]: https://www.w3schools.com/xml/xpath_nodes.asp [CSS]: https://www.w3schools.com/cssref/css_selectors.asp -[Locator function]: ./section/locator-functions.md -[Parameterized selectors]: ./section/parameterized-selectors.md \ No newline at end of file +[Locator function]: ./section/locator-functions.html +[Parameterized selectors]: ./section/parameterized-selectors.html \ No newline at end of file diff --git a/docs/section/locator-functions.md b/docs/section/locator-functions.md index d52c2fc72..3d296bd93 100644 --- a/docs/section/locator-functions.md +++ b/docs/section/locator-functions.md @@ -42,5 +42,5 @@ Given the above element definitions, you call the elements in a test just like a <!-- Link Definitions --> [Locator functions]: http://codeception.com/docs/reference/Locator -[section]: ../section.md -[parameterized selectors]: ./parameterized-selectors.md \ No newline at end of file +[section]: ../section.html +[parameterized selectors]: ./parameterized-selectors.html \ No newline at end of file diff --git a/docs/section/parameterized-selectors.md b/docs/section/parameterized-selectors.md index 58227948f..de9030031 100644 --- a/docs/section/parameterized-selectors.md +++ b/docs/section/parameterized-selectors.md @@ -152,4 +152,4 @@ Any data can be used in parameterized elements, as well as entered in test actio * `{$variable}` is a reference to data returned by a test action, like `<grabValueFrom>`. <!-- Link Definitions --> -[test]: ../test.md \ No newline at end of file +[test]: ../test.html \ No newline at end of file diff --git a/docs/suite.md b/docs/suite.md index c8b21afbf..e630ae250 100644 --- a/docs/suite.md +++ b/docs/suite.md @@ -282,12 +282,12 @@ Attributes|Type|Use|Description `remove`|boolean|optional|Removing the filter during merging. <!-- Link definitions --> -[actions]: test/actions.md -[action groups]: test/action-groups.md +[actions]: test/actions.html +[action groups]: test/action-groups.html [`<after>`]: #after-tag [`<before>`]: #before-tag -[`generate:tests`]: commands/mftf.md#generatetests -[test]: test.md +[`generate:tests`]: commands/mftf.html#generatetests +[test]: test.html [`<test>`]: #test-tag [`<group>`]: #group-tag [`<module>`]: #module-tag diff --git a/docs/test.md b/docs/test.md index 462cd138e..c0c6eb2d1 100644 --- a/docs/test.md +++ b/docs/test.md @@ -136,12 +136,12 @@ See [Action groups][action group] for more information. [`<before>`]: #before-tag [`<test>`]: #test-tag [`<tests>`]: #tests-tag -[action group]: ./test/action-groups.md -[actions]: ./test/actions.md +[action group]: ./test/action-groups.html +[actions]: ./test/actions.html [Allure]: https://github.com/allure-framework/ -[Annotations]: ./test/annotations.md -[assertion]: ./test/assertions.md +[Annotations]: ./test/annotations.html +[assertion]: ./test/assertions.html [Codeception]: https://codeception.com/docs/07-AdvancedUsage -[extend]: extending.md -[merging]: ./merging.md#insert-after -[suites]: ./suite.md \ No newline at end of file +[extend]: extending.html +[merging]: ./merging.html#insert-after +[suites]: ./suite.html \ No newline at end of file diff --git a/docs/test/action-groups.md b/docs/test/action-groups.md index 0743481f6..a3adcb613 100644 --- a/docs/test/action-groups.md +++ b/docs/test/action-groups.md @@ -258,7 +258,7 @@ Attribute|Type|Use|Description `type`|Possible values: `string`, `entity` (default).|optional|Defines the argument data type; Defaults to `entity`. <!-- Link Definitions --> -[actions]: ./actions.md -[test]: ../test.md +[actions]: ./actions.html +[test]: ../test.html [`argument`]: #argument-tag -[created]: ../data.md#persist-data \ No newline at end of file +[created]: ../data.html#persist-data \ No newline at end of file diff --git a/docs/test/actions.md b/docs/test/actions.md index 4a6c3f48e..da5dcd2a9 100644 --- a/docs/test/actions.md +++ b/docs/test/actions.md @@ -22,7 +22,7 @@ This step can be referenced within the test using `conditionalClickStep1`. The value format should met the following principles: -* Must be unique within [`<test>`](../test.md#test-tag). +* Must be unique within [`<test>`](../test.html#test-tag). * Naming should be as descriptive as possible: * Describe the action performed. * Briefly describe the purpose. @@ -166,18 +166,18 @@ The following test actions return a variable: * [grabValueFrom](#grabvaluefrom) * [executeJS](#executejs) -Learn more in [Using data returned by test actions](../data.md#use-data-returned-by-test-actions). +Learn more in [Using data returned by test actions](../data.html#use-data-returned-by-test-actions). ## Actions handling data entities -The following test actions handle data entities using [metadata](../metadata.md): +The following test actions handle data entities using [metadata](../metadata.html): * [createData](#createdata) * [deleteData](#deletedata) * [updateData](#updatedata) * [getData](#getdata) -Learn more in [Handling a REST API response](../metadata.md#rest-response). +Learn more in [Handling a REST API response](../metadata.html#rest-response). ## Reference @@ -538,7 +538,7 @@ Attribute|Type|Use|Description ### createData Creates an entity (for example, a category or product). -To create an entity, the MFTF makes a `POST` request to the Magento API according to the [data](../data.md) and [metadata](../metadata.md) of the entity to be created. +To create an entity, the MFTF makes a `POST` request to the Magento API according to the [data](../data.html) and [metadata](../metadata.html) of the entity to be created. Attribute|Type|Use|Description ---|---|---|--- @@ -617,7 +617,7 @@ Attribute|Type|Use|Description #### Examples -Delete the entity that was previously created using [`createData`](#createdata) in the scope of the [test](../test.md#test-tag). +Delete the entity that was previously created using [`createData`](#createdata) in the scope of the [test](../test.html#test-tag). 1. Create _SampleCategory_: @@ -1127,7 +1127,7 @@ Attribute|Type|Use|Description </getData> ``` -The `ProductAttributeOptionGetter` entity must be defined in the corresponding [data `*.xml`](../data.md). +The `ProductAttributeOptionGetter` entity must be defined in the corresponding [data `*.xml`](../data.html). This action can optionally contain one or more [requiredEntity](#requiredentity) child elements. @@ -1348,7 +1348,7 @@ Note that the makeScreenshot action does not automatically add the screenshot to ``` <div class="bs-callout bs-callout-info"> -This action does not add a screenshot to the Allure [report](../reporting.md).</div> +This action does not add a screenshot to the Allure [report](../reporting.html).</div> ### maximizeWindow diff --git a/docs/test/annotations.md b/docs/test/annotations.md index f30b8104e..d46cf221f 100644 --- a/docs/test/annotations.md +++ b/docs/test/annotations.md @@ -227,7 +227,7 @@ Attribute|Type|Use [setup instructions in Allure]: https://github.com/allure-framework/allure1/wiki/Test-Case-ID [severity]: #severity [stories]: #stories -[suite]: ../suite.md -[tests]: ../test.md +[suite]: ../suite.html +[tests]: ../test.html [title]: #title [skip]: #skip diff --git a/docs/test/assertions.md b/docs/test/assertions.md index 17a3d194a..6a933c73f 100644 --- a/docs/test/assertions.md +++ b/docs/test/assertions.md @@ -1,15 +1,15 @@ # Assertions -Assertions serve to pass or fail the [test](../test.md#test-tag) if a condition is not met. These assertions will look familiar to you if you've used any other testing framework, like PHPUnit. +Assertions serve to pass or fail the [test](../test.html#test-tag) if a condition is not met. These assertions will look familiar to you if you've used any other testing framework, like PHPUnit. -All assertions contain the same [common actions attributes](./actions.md#common-attributes): `stepKey`, `before`, and `after`. +All assertions contain the same [common actions attributes](./actions.html#common-attributes): `stepKey`, `before`, and `after`. Most assertions contain a `message` attribute that specifies the text of an informational message to help you identify the cause of the failure. ## Principles -The [principles for actions](../test.md#principles) are also applicable to assertions. +The [principles for actions](../test.html#principles) are also applicable to assertions. Assertion actions have nested self-descriptive elements, `<expectedResult>` and `<actualResult>`. These elements contain a result type and a value: diff --git a/docs/update.md b/docs/update.md index 1a3137eb6..3a32492f2 100644 --- a/docs/update.md +++ b/docs/update.md @@ -75,6 +75,6 @@ To update the MFTF from the previous minor version: <!-- Link Definitions --> [Changelog]: https://github.com/magento/magento2-functional-testing-framework/blob/master/CHANGELOG.md -[WYSIWYG settings]: getting-started.md#wysiwyg-settings -[Security settings]: getting-started.md#security-settings -[Find your version]: introduction.md#find-your-mftf-version \ No newline at end of file +[WYSIWYG settings]: getting-started.html#wysiwyg-settings +[Security settings]: getting-started.html#security-settings +[Find your version]: introduction.html#find-your-mftf-version \ No newline at end of file From d952d0cb6f834d6fc0f60c82fd6812b84107c6ed Mon Sep 17 00:00:00 2001 From: Donald Booth <dobooth@adobe.com> Date: Wed, 20 Mar 2019 16:31:36 -0500 Subject: [PATCH 17/18] Fixed codeception paths. --- docs/commands/codeception.md | 8 ++++---- docs/getting-started.md | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/commands/codeception.md b/docs/commands/codeception.md index d3b52216f..331e822a7 100644 --- a/docs/commands/codeception.md +++ b/docs/commands/codeception.md @@ -12,19 +12,19 @@ To run the Codeception testing framework commands directly, change your director Run all the generated tests: ```bash -vendor/bin/codecept run functional +../../../vendor/bin/codecept run functional ``` Run all tests without the `<group value="skip"/>` [annotation][]: ```bash -vendor/bin/codecept run functional --skip-group skip +../../../vendor/bin/codecept run functional --skip-group skip ``` Run all tests with the `<group value="example"/>` [annotation][] but with no `<group value="skpip"/>`: ```bash -vendor/bin/codecept run functional --group example --skip-group skip +../../../vendor/bin/codecept run functional --group example --skip-group skip ``` ## `codecept run` @@ -32,7 +32,7 @@ vendor/bin/codecept run functional --group example --skip-group skip `codecept run` runs the test suites: ```bash -vendor/bin/codecept run +../../../vendor/bin/codecept run ``` <div class="bs-callout bs-callout-info"> diff --git a/docs/getting-started.md b/docs/getting-started.md index 395e1824c..a005ac43b 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -183,7 +183,7 @@ cd dev/tests/acceptance ``` ```bash -vendor/bin/codecept run functional +../../../vendor/bin/codecept run functional ``` See more commands in [`codecept`][]. From cb5a2ba0cd8b8f131729167e636336e4c9278635 Mon Sep 17 00:00:00 2001 From: Donald Booth <dobooth@adobe.com> Date: Wed, 20 Mar 2019 16:40:34 -0500 Subject: [PATCH 18/18] Added flag per Pangolins --- docs/commands/codeception.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/commands/codeception.md b/docs/commands/codeception.md index 331e822a7..baadf1110 100644 --- a/docs/commands/codeception.md +++ b/docs/commands/codeception.md @@ -12,19 +12,19 @@ To run the Codeception testing framework commands directly, change your director Run all the generated tests: ```bash -../../../vendor/bin/codecept run functional +../../../vendor/bin/codecept run functional -c dev/tests/acceptance/codeception.yml ``` Run all tests without the `<group value="skip"/>` [annotation][]: ```bash -../../../vendor/bin/codecept run functional --skip-group skip +../../../vendor/bin/codecept run functional --skip-group skip -c dev/tests/acceptance/codeception.yml ``` Run all tests with the `<group value="example"/>` [annotation][] but with no `<group value="skpip"/>`: ```bash -../../../vendor/bin/codecept run functional --group example --skip-group skip +../../../vendor/bin/codecept run functional --group example --skip-group skip -c dev/tests/acceptance/codeception.yml ``` ## `codecept run`