diff --git a/CHANGELOG.md b/CHANGELOG.md
index 5a24b5f29..fd555ee88 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,41 @@
Magento Functional Testing Framework Changelog
================================================
+3.1.0
+________
+
+### Enhancements
+
+* Customizability
+ * Introduced the new `return` action that allows action groups to return a value. See the [actions page](./docs/test/actions.md#return) for details.
+ * Introduced new MFTF command that invokes `vendor/bin/codecept run`. See the [mftf page](./docs/commands/mftf.md#codeceptrun) for details.
+
+* Usability
+ * Introduced new action `pause`, to invoke codeception interactive pause for debugging during test execution. See the [Interactive Pause](./docs/interactive-pause.md) page for details.
+ * Introduced a new `.env` configuration option `ENABLE_PAUSE`, to enable the new pause feature.
+
+* Maintainability
+ * Added a new static check that checks for the usage of the `pause` action. See the [command page](./docs/commands/mftf.md#static-checks) for details.
+
+* Modularity
+ * MFTF now supports the use of actions from multiple modules within suites.
+
+* Traceability
+ * A deprecation notice is now added at test execution time for deprecated metadata usage.
+
+### Fixes
+
+* Fixed issue with suite precondition failure for `createData` with required entity.
+
+### GitHub Issues/Pull requests:
+
+ * [#547](https://github.com/magento/magento2-functional-testing-framework/pull/547) -- Fix invalid behavior of MAGENTO_BACKEND_BASE_URL
+ * [#742](https://github.com/magento/magento2-functional-testing-framework/pull/742) -- Fix Waits In MagentoPwaWebDriver
+ * [#750](https://github.com/magento/magento2-functional-testing-framework/pull/750) -- Docs: Outlining the difference between Allure severity levels
+ * [#683](https://github.com/magento/magento2-functional-testing-framework/pull/683) -- Docs: Renamed sample test name with the correct one
+ * [#691](https://github.com/magento/magento2-functional-testing-framework/pull/691) -- Docs: Branch name updates
+ * [#678](https://github.com/magento/magento2-functional-testing-framework/pull/678) -- Docs: Command added to modify the web server rewrites configuration
+ * [#745](https://github.com/magento/magento2-functional-testing-framework/pull/745) -- Docs: Remove invalid sample test name
+
3.0.0
---------
diff --git a/composer.json b/composer.json
index e1a04296f..813ab382b 100755
--- a/composer.json
+++ b/composer.json
@@ -2,7 +2,7 @@
"name": "magento/magento2-functional-testing-framework",
"description": "Magento2 Functional Testing Framework",
"type": "library",
- "version": "3.0.0",
+ "version": "3.1.0",
"license": "AGPL-3.0",
"keywords": ["magento", "automation", "functional", "testing"],
"config": {
@@ -24,6 +24,7 @@
"composer/composer": "^1.9",
"csharpru/vault-php": "~3.5.3",
"csharpru/vault-php-guzzle6-transport": "^2.0",
+ "hoa/console": "~3.0",
"monolog/monolog": "^1.17",
"mustache/mustache": "~2.5",
"php-webdriver/webdriver": "^1.8.0",
diff --git a/composer.lock b/composer.lock
index dff4f1006..7f7648096 100644
--- a/composer.lock
+++ b/composer.lock
@@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
- "content-hash": "a0516f6072ced659bf4ed7a486756bb2",
+ "content-hash": "278e33e2c7d183d0b7689b5a76127d29",
"packages": [
{
"name": "allure-framework/allure-codeception",
@@ -1662,6 +1662,555 @@
],
"time": "2019-07-01T23:21:34+00:00"
},
+ {
+ "name": "hoa/consistency",
+ "version": "1.17.05.02",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/hoaproject/Consistency.git",
+ "reference": "fd7d0adc82410507f332516faf655b6ed22e4c2f"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/hoaproject/Consistency/zipball/fd7d0adc82410507f332516faf655b6ed22e4c2f",
+ "reference": "fd7d0adc82410507f332516faf655b6ed22e4c2f",
+ "shasum": ""
+ },
+ "require": {
+ "hoa/exception": "~1.0",
+ "php": ">=5.5.0"
+ },
+ "require-dev": {
+ "hoa/stream": "~1.0",
+ "hoa/test": "~2.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Hoa\\Consistency\\": "."
+ },
+ "files": [
+ "Prelude.php"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Ivan Enderlin",
+ "email": "ivan.enderlin@hoa-project.net"
+ },
+ {
+ "name": "Hoa community",
+ "homepage": "https://hoa-project.net/"
+ }
+ ],
+ "description": "The Hoa\\Consistency library.",
+ "homepage": "https://hoa-project.net/",
+ "keywords": [
+ "autoloader",
+ "callable",
+ "consistency",
+ "entity",
+ "flex",
+ "keyword",
+ "library"
+ ],
+ "time": "2017-05-02T12:18:12+00:00"
+ },
+ {
+ "name": "hoa/console",
+ "version": "3.17.05.02",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/hoaproject/Console.git",
+ "reference": "e231fd3ea70e6d773576ae78de0bdc1daf331a66"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/hoaproject/Console/zipball/e231fd3ea70e6d773576ae78de0bdc1daf331a66",
+ "reference": "e231fd3ea70e6d773576ae78de0bdc1daf331a66",
+ "shasum": ""
+ },
+ "require": {
+ "hoa/consistency": "~1.0",
+ "hoa/event": "~1.0",
+ "hoa/exception": "~1.0",
+ "hoa/file": "~1.0",
+ "hoa/protocol": "~1.0",
+ "hoa/stream": "~1.0",
+ "hoa/ustring": "~4.0"
+ },
+ "require-dev": {
+ "hoa/test": "~2.0"
+ },
+ "suggest": {
+ "ext-pcntl": "To enable hoa://Event/Console/Window:resize.",
+ "hoa/dispatcher": "To use the console kit.",
+ "hoa/router": "To use the console kit."
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "3.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Hoa\\Console\\": "."
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Ivan Enderlin",
+ "email": "ivan.enderlin@hoa-project.net"
+ },
+ {
+ "name": "Hoa community",
+ "homepage": "https://hoa-project.net/"
+ }
+ ],
+ "description": "The Hoa\\Console library.",
+ "homepage": "https://hoa-project.net/",
+ "keywords": [
+ "autocompletion",
+ "chrome",
+ "cli",
+ "console",
+ "cursor",
+ "getoption",
+ "library",
+ "option",
+ "parser",
+ "processus",
+ "readline",
+ "terminfo",
+ "tput",
+ "window"
+ ],
+ "time": "2017-05-02T12:26:19+00:00"
+ },
+ {
+ "name": "hoa/event",
+ "version": "1.17.01.13",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/hoaproject/Event.git",
+ "reference": "6c0060dced212ffa3af0e34bb46624f990b29c54"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/hoaproject/Event/zipball/6c0060dced212ffa3af0e34bb46624f990b29c54",
+ "reference": "6c0060dced212ffa3af0e34bb46624f990b29c54",
+ "shasum": ""
+ },
+ "require": {
+ "hoa/consistency": "~1.0",
+ "hoa/exception": "~1.0"
+ },
+ "require-dev": {
+ "hoa/test": "~2.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Hoa\\Event\\": "."
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Ivan Enderlin",
+ "email": "ivan.enderlin@hoa-project.net"
+ },
+ {
+ "name": "Hoa community",
+ "homepage": "https://hoa-project.net/"
+ }
+ ],
+ "description": "The Hoa\\Event library.",
+ "homepage": "https://hoa-project.net/",
+ "keywords": [
+ "event",
+ "library",
+ "listener",
+ "observer"
+ ],
+ "time": "2017-01-13T15:30:50+00:00"
+ },
+ {
+ "name": "hoa/exception",
+ "version": "1.17.01.16",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/hoaproject/Exception.git",
+ "reference": "091727d46420a3d7468ef0595651488bfc3a458f"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/hoaproject/Exception/zipball/091727d46420a3d7468ef0595651488bfc3a458f",
+ "reference": "091727d46420a3d7468ef0595651488bfc3a458f",
+ "shasum": ""
+ },
+ "require": {
+ "hoa/consistency": "~1.0",
+ "hoa/event": "~1.0"
+ },
+ "require-dev": {
+ "hoa/test": "~2.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Hoa\\Exception\\": "."
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Ivan Enderlin",
+ "email": "ivan.enderlin@hoa-project.net"
+ },
+ {
+ "name": "Hoa community",
+ "homepage": "https://hoa-project.net/"
+ }
+ ],
+ "description": "The Hoa\\Exception library.",
+ "homepage": "https://hoa-project.net/",
+ "keywords": [
+ "exception",
+ "library"
+ ],
+ "time": "2017-01-16T07:53:27+00:00"
+ },
+ {
+ "name": "hoa/file",
+ "version": "1.17.07.11",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/hoaproject/File.git",
+ "reference": "35cb979b779bc54918d2f9a4e02ed6c7a1fa67ca"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/hoaproject/File/zipball/35cb979b779bc54918d2f9a4e02ed6c7a1fa67ca",
+ "reference": "35cb979b779bc54918d2f9a4e02ed6c7a1fa67ca",
+ "shasum": ""
+ },
+ "require": {
+ "hoa/consistency": "~1.0",
+ "hoa/event": "~1.0",
+ "hoa/exception": "~1.0",
+ "hoa/iterator": "~2.0",
+ "hoa/stream": "~1.0"
+ },
+ "require-dev": {
+ "hoa/test": "~2.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Hoa\\File\\": "."
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Ivan Enderlin",
+ "email": "ivan.enderlin@hoa-project.net"
+ },
+ {
+ "name": "Hoa community",
+ "homepage": "https://hoa-project.net/"
+ }
+ ],
+ "description": "The Hoa\\File library.",
+ "homepage": "https://hoa-project.net/",
+ "keywords": [
+ "Socket",
+ "directory",
+ "file",
+ "finder",
+ "library",
+ "link",
+ "temporary"
+ ],
+ "time": "2017-07-11T07:42:15+00:00"
+ },
+ {
+ "name": "hoa/iterator",
+ "version": "2.17.01.10",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/hoaproject/Iterator.git",
+ "reference": "d1120ba09cb4ccd049c86d10058ab94af245f0cc"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/hoaproject/Iterator/zipball/d1120ba09cb4ccd049c86d10058ab94af245f0cc",
+ "reference": "d1120ba09cb4ccd049c86d10058ab94af245f0cc",
+ "shasum": ""
+ },
+ "require": {
+ "hoa/consistency": "~1.0",
+ "hoa/exception": "~1.0"
+ },
+ "require-dev": {
+ "hoa/test": "~2.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "2.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Hoa\\Iterator\\": "."
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Ivan Enderlin",
+ "email": "ivan.enderlin@hoa-project.net"
+ },
+ {
+ "name": "Hoa community",
+ "homepage": "https://hoa-project.net/"
+ }
+ ],
+ "description": "The Hoa\\Iterator library.",
+ "homepage": "https://hoa-project.net/",
+ "keywords": [
+ "iterator",
+ "library"
+ ],
+ "time": "2017-01-10T10:34:47+00:00"
+ },
+ {
+ "name": "hoa/protocol",
+ "version": "1.17.01.14",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/hoaproject/Protocol.git",
+ "reference": "5c2cf972151c45f373230da170ea015deecf19e2"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/hoaproject/Protocol/zipball/5c2cf972151c45f373230da170ea015deecf19e2",
+ "reference": "5c2cf972151c45f373230da170ea015deecf19e2",
+ "shasum": ""
+ },
+ "require": {
+ "hoa/consistency": "~1.0",
+ "hoa/exception": "~1.0"
+ },
+ "require-dev": {
+ "hoa/test": "~2.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Hoa\\Protocol\\": "."
+ },
+ "files": [
+ "Wrapper.php"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Ivan Enderlin",
+ "email": "ivan.enderlin@hoa-project.net"
+ },
+ {
+ "name": "Hoa community",
+ "homepage": "https://hoa-project.net/"
+ }
+ ],
+ "description": "The Hoa\\Protocol library.",
+ "homepage": "https://hoa-project.net/",
+ "keywords": [
+ "library",
+ "protocol",
+ "resource",
+ "stream",
+ "wrapper"
+ ],
+ "time": "2017-01-14T12:26:10+00:00"
+ },
+ {
+ "name": "hoa/stream",
+ "version": "1.17.02.21",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/hoaproject/Stream.git",
+ "reference": "3293cfffca2de10525df51436adf88a559151d82"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/hoaproject/Stream/zipball/3293cfffca2de10525df51436adf88a559151d82",
+ "reference": "3293cfffca2de10525df51436adf88a559151d82",
+ "shasum": ""
+ },
+ "require": {
+ "hoa/consistency": "~1.0",
+ "hoa/event": "~1.0",
+ "hoa/exception": "~1.0",
+ "hoa/protocol": "~1.0"
+ },
+ "require-dev": {
+ "hoa/test": "~2.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Hoa\\Stream\\": "."
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Ivan Enderlin",
+ "email": "ivan.enderlin@hoa-project.net"
+ },
+ {
+ "name": "Hoa community",
+ "homepage": "https://hoa-project.net/"
+ }
+ ],
+ "description": "The Hoa\\Stream library.",
+ "homepage": "https://hoa-project.net/",
+ "keywords": [
+ "Context",
+ "bucket",
+ "composite",
+ "filter",
+ "in",
+ "library",
+ "out",
+ "protocol",
+ "stream",
+ "wrapper"
+ ],
+ "time": "2017-02-21T16:01:06+00:00"
+ },
+ {
+ "name": "hoa/ustring",
+ "version": "4.17.01.16",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/hoaproject/Ustring.git",
+ "reference": "e6326e2739178799b1fe3fdd92029f9517fa17a0"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/hoaproject/Ustring/zipball/e6326e2739178799b1fe3fdd92029f9517fa17a0",
+ "reference": "e6326e2739178799b1fe3fdd92029f9517fa17a0",
+ "shasum": ""
+ },
+ "require": {
+ "hoa/consistency": "~1.0",
+ "hoa/exception": "~1.0"
+ },
+ "require-dev": {
+ "hoa/test": "~2.0"
+ },
+ "suggest": {
+ "ext-iconv": "ext/iconv must be present (or a third implementation) to use Hoa\\Ustring::transcode().",
+ "ext-intl": "To get a better Hoa\\Ustring::toAscii() and Hoa\\Ustring::compareTo()."
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "4.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Hoa\\Ustring\\": "."
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Ivan Enderlin",
+ "email": "ivan.enderlin@hoa-project.net"
+ },
+ {
+ "name": "Hoa community",
+ "homepage": "https://hoa-project.net/"
+ }
+ ],
+ "description": "The Hoa\\Ustring library.",
+ "homepage": "https://hoa-project.net/",
+ "keywords": [
+ "library",
+ "search",
+ "string",
+ "unicode"
+ ],
+ "time": "2017-01-16T07:08:25+00:00"
+ },
{
"name": "jms/metadata",
"version": "1.7.0",
@@ -3115,6 +3664,7 @@
"keywords": [
"tokenizer"
],
+ "abandoned": true,
"time": "2020-02-07T06:19:00+00:00"
},
{
@@ -6920,5 +7470,6 @@
"ext-json": "*",
"ext-openssl": "*"
},
- "platform-dev": []
+ "platform-dev": [],
+ "plugin-api-version": "1.1.0"
}
diff --git a/dev/tests/_bootstrap.php b/dev/tests/_bootstrap.php
index 059441721..9d3750ec6 100644
--- a/dev/tests/_bootstrap.php
+++ b/dev/tests/_bootstrap.php
@@ -9,9 +9,11 @@
$vendorAutoloadPath = realpath(PROJECT_ROOT . '/vendor/autoload.php');
$mftfTestCasePath = realpath(PROJECT_ROOT . '/dev/tests/util/MftfTestCase.php');
+$mftfStaticTestCasePath = realpath(PROJECT_ROOT . '/dev/tests/util/MftfStaticTestCase.php');
require_once $vendorAutoloadPath;
require_once $mftfTestCasePath;
+require_once $mftfStaticTestCasePath;
// Set up AspectMock
$kernel = \AspectMock\Kernel::getInstance();
diff --git a/dev/tests/unit/Magento/FunctionalTestFramework/DataGenerator/Handlers/DataObjectHandlerTest.php b/dev/tests/unit/Magento/FunctionalTestFramework/DataGenerator/Handlers/DataObjectHandlerTest.php
index c9ec92ba8..745789501 100644
--- a/dev/tests/unit/Magento/FunctionalTestFramework/DataGenerator/Handlers/DataObjectHandlerTest.php
+++ b/dev/tests/unit/Magento/FunctionalTestFramework/DataGenerator/Handlers/DataObjectHandlerTest.php
@@ -13,12 +13,22 @@
use Magento\FunctionalTestingFramework\ObjectManager;
use Magento\FunctionalTestingFramework\ObjectManagerFactory;
use tests\unit\Util\MagentoTestCase;
+use tests\unit\Util\ObjectHandlerUtil;
+use tests\unit\Util\TestLoggingUtil;
/**
* Class DataObjectHandlerTest
*/
class DataObjectHandlerTest extends MagentoTestCase
{
+ /**
+ * Setup method
+ */
+ public function setUp(): void
+ {
+ TestLoggingUtil::getInstance()->setMockLoggingUtil();
+ }
+
// All tests share this array, feel free to add but be careful modifying or removing
const PARSER_OUTPUT = [
'entity' => [
@@ -44,6 +54,22 @@ class DataObjectHandlerTest extends MagentoTestCase
]
];
+ const PARSER_OUTPUT_DEPRECATED = [
+ 'entity' => [
+ 'EntityOne' => [
+ 'type' => 'testType',
+ 'data' => [
+ 0 => [
+ 'key' => 'testKey',
+ 'value' => 'testValue'
+ ]
+ ],
+ 'deprecated' => "deprecation message",
+ 'filename' => "filename.xml"
+ ],
+ ]
+ ];
+
const PARSER_OUTPUT_WITH_EXTEND = [
'entity' => [
'EntityOne' => [
@@ -123,7 +149,7 @@ class DataObjectHandlerTest extends MagentoTestCase
*/
public function testGetAllObjects()
{
- $this->setUpMockDataObjectHander(self::PARSER_OUTPUT);
+ ObjectHandlerUtil::mockDataObjectHandlerWithData(self::PARSER_OUTPUT);
// Call the method under test
$actual = DataObjectHandler::getInstance()->getAllObjects();
@@ -134,12 +160,30 @@ public function testGetAllObjects()
$this->assertEquals($expected, $actual['EntityOne']);
}
+ /**
+ * test deprecated data object
+ */
+ public function testDeprecatedDataObject()
+ {
+ ObjectHandlerUtil::mockDataObjectHandlerWithData(self::PARSER_OUTPUT_DEPRECATED);
+
+ // Call the method under test
+ $actual = DataObjectHandler::getInstance()->getAllObjects();
+
+ //validate deprecation warning
+ TestLoggingUtil::getInstance()->validateMockLogStatement(
+ 'warning',
+ "DEPRECATION: The data entity 'EntityOne' is deprecated.",
+ ["fileName" => "filename.xml", "deprecatedMessage" => "deprecation message"]
+ );
+ }
+
/**
* getObject should return the expected data object if it exists
*/
public function testGetObject()
{
- $this->setUpMockDataObjectHander(self::PARSER_OUTPUT);
+ ObjectHandlerUtil::mockDataObjectHandlerWithData(self::PARSER_OUTPUT);
// Call the method under test
$actual = DataObjectHandler::getInstance()->getObject('EntityOne');
@@ -154,7 +198,7 @@ public function testGetObject()
*/
public function testGetObjectNull()
{
- $this->setUpMockDataObjectHander(self::PARSER_OUTPUT);
+ ObjectHandlerUtil::mockDataObjectHandlerWithData(self::PARSER_OUTPUT);
$actual = DataObjectHandler::getInstance()->getObject('h953u789h0g73t521'); // doesnt exist
$this->assertNull($actual);
@@ -165,7 +209,7 @@ public function testGetObjectNull()
*/
public function testGetAllObjectsWithDataExtends()
{
- $this->setUpMockDataObjectHander(self::PARSER_OUTPUT_WITH_EXTEND);
+ ObjectHandlerUtil::mockDataObjectHandlerWithData(self::PARSER_OUTPUT_WITH_EXTEND);
// Call the method under test
$actual = DataObjectHandler::getInstance()->getAllObjects();
@@ -189,7 +233,7 @@ public function testGetAllObjectsWithDataExtends()
*/
public function testGetObjectWithDataExtends()
{
- $this->setUpMockDataObjectHander(self::PARSER_OUTPUT_WITH_EXTEND);
+ ObjectHandlerUtil::mockDataObjectHandlerWithData(self::PARSER_OUTPUT_WITH_EXTEND);
// Call the method under test
$actual = DataObjectHandler::getInstance()->getObject('EntityTwo');
@@ -212,7 +256,7 @@ public function testGetObjectWithDataExtends()
*/
public function testGetAllObjectsWithDataExtendsItself()
{
- $this->setUpMockDataObjectHander(self::PARSER_OUTPUT_WITH_EXTEND_INVALID);
+ ObjectHandlerUtil::mockDataObjectHandlerWithData(self::PARSER_OUTPUT_WITH_EXTEND_INVALID);
$this->expectException(\Magento\FunctionalTestingFramework\Exceptions\TestFrameworkException::class);
$this->expectExceptionMessage(
@@ -229,7 +273,7 @@ public function testGetAllObjectsWithDataExtendsItself()
*/
public function testGetObjectWithDataExtendsItself()
{
- $this->setUpMockDataObjectHander(self::PARSER_OUTPUT_WITH_EXTEND_INVALID);
+ ObjectHandlerUtil::mockDataObjectHandlerWithData(self::PARSER_OUTPUT_WITH_EXTEND_INVALID);
$this->expectException(\Magento\FunctionalTestingFramework\Exceptions\TestFrameworkException::class);
$this->expectExceptionMessage(
@@ -244,29 +288,10 @@ public function testGetObjectWithDataExtendsItself()
}
/**
- * Set up everything required to mock DataObjectHander::getInstance()
- * The first call to getInstance() uses these mocks to emulate the parser, initializing internal state
- * according to the PARSER_OUTPUT value
- *
- * @param array $entityDataArray
+ * clean up function runs after all tests
*/
- private function setUpMockDataObjectHander($entityDataArray)
+ public static function tearDownAfterClass(): void
{
- // Clear DataObjectHandler singleton if already set
- $property = new \ReflectionProperty(DataObjectHandler::class, "INSTANCE");
- $property->setAccessible(true);
- $property->setValue(null);
-
- $mockDataProfileSchemaParser = AspectMock::double(DataProfileSchemaParser::class, [
- 'readDataProfiles' => $entityDataArray
- ])->make();
-
- $mockObjectManager = AspectMock::double(ObjectManager::class, [
- 'create' => $mockDataProfileSchemaParser
- ])->make();
-
- AspectMock::double(ObjectManagerFactory::class, [
- 'getObjectManager' => $mockObjectManager
- ]);
+ TestLoggingUtil::getInstance()->clearMockLoggingUtil();
}
}
diff --git a/dev/tests/unit/Magento/FunctionalTestFramework/DataGenerator/Handlers/OperationDefinitionObjectHandlerTest.php b/dev/tests/unit/Magento/FunctionalTestFramework/DataGenerator/Handlers/OperationDefinitionObjectHandlerTest.php
index 9ca10c7f2..35b06a24a 100644
--- a/dev/tests/unit/Magento/FunctionalTestFramework/DataGenerator/Handlers/OperationDefinitionObjectHandlerTest.php
+++ b/dev/tests/unit/Magento/FunctionalTestFramework/DataGenerator/Handlers/OperationDefinitionObjectHandlerTest.php
@@ -14,12 +14,22 @@
use Magento\FunctionalTestingFramework\DataGenerator\Handlers\OperationDefinitionObjectHandler;
use Magento\FunctionalTestingFramework\DataGenerator\Parsers\OperationDefinitionParser;
use tests\unit\Util\MagentoTestCase;
+use tests\unit\Util\ObjectHandlerUtil;
+use tests\unit\Util\TestLoggingUtil;
/**
* Class OperationDefinitionObjectHandlerTest
*/
class OperationDefinitionObjectHandlerTest extends MagentoTestCase
{
+ /**
+ * Setup method
+ */
+ public function setUp(): void
+ {
+ TestLoggingUtil::getInstance()->setMockLoggingUtil();
+ }
+
public function testGetMultipleObjects()
{
// Data Variables for Assertions
@@ -63,7 +73,7 @@ public function testGetMultipleObjects()
],
]
]]];
- $this->setMockParserOutput($mockData);
+ ObjectHandlerUtil::mockOperationHandlerWithData($mockData);
//Perform Assertions
$operationDefinitionManager = OperationDefinitionObjectHandler::getInstance();
@@ -72,6 +82,56 @@ public function testGetMultipleObjects()
$this->assertArrayHasKey($operationType2 . $dataType1, $operations);
}
+ public function testDeprecatedOperation()
+ {
+ // Data Variables for Assertions
+ $dataType1 = "type1";
+ $operationType1 = "create";
+
+ /**
+ * Parser Output. Just one metadata with 1 field
+ * operationName
+ * createType1
+ * has field
+ * key=id, value=integer
+ */
+ $mockData = [OperationDefinitionObjectHandler::ENTITY_OPERATION_ROOT_TAG => [
+ "testOperationName" => [
+ OperationDefinitionObjectHandler::ENTITY_OPERATION_DATA_TYPE => $dataType1,
+ OperationDefinitionObjectHandler::ENTITY_OPERATION_TYPE => $operationType1,
+ OperationDefinitionObjectHandler::ENTITY_OPERATION_AUTH => "auth",
+ OperationDefinitionObjectHandler::ENTITY_OPERATION_URL => "V1/Type1",
+ OperationDefinitionObjectHandler::ENTITY_OPERATION_METHOD => "POST",
+ OperationDefinitionObjectHandler::ENTITY_OPERATION_ENTRY => [
+ 0 => [
+ OperationDefinitionObjectHandler::ENTITY_OPERATION_ENTRY_KEY => "id",
+ OperationDefinitionObjectHandler::ENTITY_OPERATION_ENTRY_VALUE => "integer"
+ ],
+ ],
+ OperationDefinitionObjectHandler::OBJ_DEPRECATED => 'deprecation message'
+ ]]];
+ ObjectHandlerUtil::mockOperationHandlerWithData($mockData);
+
+ //Perform Assertions
+ $operationDefinitionManager = OperationDefinitionObjectHandler::getInstance();
+ $operations = $operationDefinitionManager->getAllObjects();
+
+ $this->assertArrayHasKey($operationType1 . $dataType1, $operations);
+ TestLoggingUtil::getInstance()->validateMockLogStatement(
+ 'notice',
+ "NOTICE: 1 metadata operation name violations detected. See mftf.log for details.",
+ []
+ );
+ // test run time deprecation notice
+ $operation = $operationDefinitionManager->getOperationDefinition($operationType1, $dataType1);
+ $operation->logDeprecated();
+ TestLoggingUtil::getInstance()->validateMockLogStatement(
+ 'warning',
+ "DEPRECATION: The operation testOperationName is deprecated.",
+ ['operationType' => 'create', 'deprecatedMessage' => 'deprecation message']
+ );
+ }
+
public function testObjectCreation()
{
// Data Variables for Assertions
@@ -180,7 +240,7 @@ public function testObjectCreation()
);
// Set up mocked data output
- $this->setMockParserOutput($mockData);
+ ObjectHandlerUtil::mockOperationHandlerWithData($mockData);
// Get Operation
$operationDefinitionManager = OperationDefinitionObjectHandler::getInstance();
@@ -278,7 +338,7 @@ public function testObjectArrayCreation()
);
// Set up mocked data output
- $this->setMockParserOutput($mockData);
+ ObjectHandlerUtil::mockOperationHandlerWithData($mockData);
// Get Operation
$operationDefinitionManager = OperationDefinitionObjectHandler::getInstance();
@@ -346,7 +406,7 @@ public function testLooseJsonCreation()
);
// Set up mocked data output
- $this->setMockParserOutput($mockData);
+ ObjectHandlerUtil::mockOperationHandlerWithData($mockData);
// get Operations
$operationDefinitionManager = OperationDefinitionObjectHandler::getInstance();
@@ -358,25 +418,10 @@ public function testLooseJsonCreation()
}
/**
- * Function used to set mock for parser return and force init method to run between tests.
- *
- * @param array $data
+ * clean up function runs after all tests
*/
- private function setMockParserOutput($data)
+ public static function tearDownAfterClass(): void
{
- // clear Operation object handler value to inject parsed content
- $property = new \ReflectionProperty(
- OperationDefinitionObjectHandler::class,
- 'INSTANCE'
- );
- $property->setAccessible(true);
- $property->setValue(null);
-
- $mockOperationParser = AspectMock::double(
- OperationDefinitionParser::class,
- ["readOperationMetadata" => $data]
- )->make();
- $instance = AspectMock::double(ObjectManager::class, ['create' => $mockOperationParser])->make();
- AspectMock::double(ObjectManagerFactory::class, ['getObjectManager' => $instance]);
+ TestLoggingUtil::getInstance()->clearMockLoggingUtil();
}
}
diff --git a/dev/tests/unit/Magento/FunctionalTestFramework/DataGenerator/Handlers/PersistedObjectHandlerTest.php b/dev/tests/unit/Magento/FunctionalTestFramework/DataGenerator/Handlers/PersistedObjectHandlerTest.php
index 870e75dcd..7e39fe216 100644
--- a/dev/tests/unit/Magento/FunctionalTestFramework/DataGenerator/Handlers/PersistedObjectHandlerTest.php
+++ b/dev/tests/unit/Magento/FunctionalTestFramework/DataGenerator/Handlers/PersistedObjectHandlerTest.php
@@ -16,6 +16,7 @@
use Magento\FunctionalTestingFramework\ObjectManager;
use Magento\FunctionalTestingFramework\ObjectManagerFactory;
use tests\unit\Util\MagentoTestCase;
+use tests\unit\Util\ObjectHandlerUtil;
use tests\unit\Util\TestLoggingUtil;
/**
@@ -84,7 +85,7 @@ public function testCreateSimpleEntity()
";
// Mock Classes
- $this->mockDataHandlerWithOutput($parserOutput);
+ ObjectHandlerUtil::mockDataObjectHandlerWithData($parserOutput);
$this->mockCurlHandler($jsonResponse);
$handler = PersistedObjectHandler::getInstance();
@@ -127,7 +128,7 @@ public function testDeleteSimpleEntity()
";
// Mock Classes
- $this->mockDataHandlerWithOutput($parserOutput);
+ ObjectHandlerUtil::mockDataObjectHandlerWithData($parserOutput);
$this->mockCurlHandler($jsonResponse);
$handler = PersistedObjectHandler::getInstance();
@@ -175,7 +176,7 @@ public function testGetSimpleEntity()
";
// Mock Classes
- $this->mockDataHandlerWithOutput($parserOutput);
+ ObjectHandlerUtil::mockDataObjectHandlerWithData($parserOutput);
$this->mockCurlHandler($jsonResponse);
$handler = PersistedObjectHandler::getInstance();
@@ -235,7 +236,7 @@ public function testUpdateSimpleEntity()
";
// Mock Classes
- $this->mockDataHandlerWithOutput($parserOutput);
+ ObjectHandlerUtil::mockDataObjectHandlerWithData($parserOutput);
$this->mockCurlHandler($jsonResponse);
$handler = PersistedObjectHandler::getInstance();
$handler->createEntity(
@@ -322,7 +323,7 @@ public function testRetrieveEntityAcrossScopes()
// Mock Classes and Create Entities
$handler = PersistedObjectHandler::getInstance();
- $this->mockDataHandlerWithOutput($parserOutputOne);
+ ObjectHandlerUtil::mockDataObjectHandlerWithData($parserOutputOne);
$this->mockCurlHandler($jsonReponseOne);
$handler->createEntity(
$entityStepKeyOne,
@@ -399,7 +400,7 @@ public function testRetrieveEntityValidField($name, $key, $value, $type, $scope,
// Mock Classes and Create Entities
$handler = PersistedObjectHandler::getInstance();
- $this->mockDataHandlerWithOutput($parserOutputOne);
+ ObjectHandlerUtil::mockDataObjectHandlerWithData($parserOutputOne);
$this->mockCurlHandler($jsonReponseOne);
$handler->createEntity($stepKey, $scope, $name);
@@ -447,8 +448,7 @@ public function testRetrieveEntityInValidField($name, $key, $value, $type, $scop
// Mock Classes and Create Entities
$handler = PersistedObjectHandler::getInstance();
-
- $this->mockDataHandlerWithOutput($parserOutputOne);
+ ObjectHandlerUtil::mockDataObjectHandlerWithData($parserOutputOne);
$this->mockCurlHandler($jsonReponseOne);
$handler->createEntity($stepKey, $scope, $name);
@@ -475,31 +475,6 @@ public static function entityDataProvider()
];
}
- /**
- * Mocks DataObjectHandler to use given output to create
- * @param $parserOutput
- * @throws \Exception
- */
- public function mockDataHandlerWithOutput($parserOutput)
- {
- // Clear DataObjectHandler singleton if already set
- $property = new \ReflectionProperty(DataObjectHandler::class, "INSTANCE");
- $property->setAccessible(true);
- $property->setValue(null);
-
- $mockDataProfileSchemaParser = AspectMock::double(DataProfileSchemaParser::class, [
- 'readDataProfiles' => $parserOutput
- ])->make();
-
- $mockObjectManager = AspectMock::double(ObjectManager::class, [
- 'create' => $mockDataProfileSchemaParser
- ])->make();
-
- AspectMock::double(ObjectManagerFactory::class, [
- 'getObjectManager' => $mockObjectManager
- ]);
- }
-
public function mockCurlHandler($response)
{
AspectMock::double(CurlHandler::class, [
diff --git a/dev/tests/unit/Magento/FunctionalTestFramework/Page/Handlers/PageObjectHandlerTest.php b/dev/tests/unit/Magento/FunctionalTestFramework/Page/Handlers/PageObjectHandlerTest.php
index 1a0ba5fc2..eb305bc4d 100644
--- a/dev/tests/unit/Magento/FunctionalTestFramework/Page/Handlers/PageObjectHandlerTest.php
+++ b/dev/tests/unit/Magento/FunctionalTestFramework/Page/Handlers/PageObjectHandlerTest.php
@@ -12,9 +12,19 @@
use Magento\FunctionalTestingFramework\Page\Handlers\PageObjectHandler;
use Magento\FunctionalTestingFramework\XmlParser\PageParser;
use tests\unit\Util\MagentoTestCase;
+use tests\unit\Util\ObjectHandlerUtil;
+use tests\unit\Util\TestLoggingUtil;
class PageObjectHandlerTest extends MagentoTestCase
{
+ /**
+ * Setup method
+ */
+ public function setUp(): void
+ {
+ TestLoggingUtil::getInstance()->setMockLoggingUtil();
+ }
+
public function testGetPageObject()
{
$mockData = [
@@ -36,7 +46,7 @@ public function testGetPageObject()
],
"area" => "test"
]];
- $this->setMockParserOutput($mockData);
+ ObjectHandlerUtil::mockPageObjectHandlerWithData($mockData);
// get pages
$pageHandler = PageObjectHandler::getInstance();
@@ -61,7 +71,7 @@ public function testGetEmptyPage()
],
"area" => "test"
]];
- $this->setMockParserOutput($mockData);
+ ObjectHandlerUtil::mockPageObjectHandlerWithData($mockData);
// get pages
$page = PageObjectHandler::getInstance()->getObject('testPage1');
@@ -70,20 +80,35 @@ public function testGetEmptyPage()
$this->addToAssertionCount(1);
}
+ public function testDeprecatedPage()
+ {
+ $mockData = [
+ "testPage1" => [
+ "url" => "testURL1",
+ "module" => "testModule1",
+ "section" => [
+ ],
+ "area" => "test",
+ "deprecated" => "deprecation message",
+ "filename" => "filename.xml"
+ ]];
+ ObjectHandlerUtil::mockPageObjectHandlerWithData($mockData);
+
+ // get pages
+ $page = PageObjectHandler::getInstance()->getObject('testPage1');
+
+ TestLoggingUtil::getInstance()->validateMockLogStatement(
+ 'notice',
+ "NOTICE: 1 Page name violations detected. See mftf.log for details.",
+ []
+ );
+ }
+
/**
- * Function used to set mock for parser return and force init method to run between tests.
- *
- * @param array $data
+ * clean up function runs after all tests
*/
- private function setMockParserOutput($data)
+ public static function tearDownAfterClass(): void
{
- // clear section object handler value to inject parsed content
- $property = new \ReflectionProperty(PageObjectHandler::class, 'INSTANCE');
- $property->setAccessible(true);
- $property->setValue(null);
-
- $mockSectionParser = AspectMock::double(PageParser::class, ["getData" => $data])->make();
- $instance = AspectMock::double(ObjectManager::class, ['get' => $mockSectionParser])->make();
- AspectMock::double(ObjectManagerFactory::class, ['getObjectManager' => $instance]);
+ TestLoggingUtil::getInstance()->clearMockLoggingUtil();
}
}
diff --git a/dev/tests/unit/Magento/FunctionalTestFramework/Page/Handlers/SectionObjectHandlerTest.php b/dev/tests/unit/Magento/FunctionalTestFramework/Page/Handlers/SectionObjectHandlerTest.php
index b8bcf3dfb..69088944a 100644
--- a/dev/tests/unit/Magento/FunctionalTestFramework/Page/Handlers/SectionObjectHandlerTest.php
+++ b/dev/tests/unit/Magento/FunctionalTestFramework/Page/Handlers/SectionObjectHandlerTest.php
@@ -12,9 +12,19 @@
use Magento\FunctionalTestingFramework\Page\Handlers\SectionObjectHandler;
use Magento\FunctionalTestingFramework\XmlParser\SectionParser;
use tests\unit\Util\MagentoTestCase;
+use tests\unit\Util\ObjectHandlerUtil;
+use tests\unit\Util\TestLoggingUtil;
class SectionObjectHandlerTest extends MagentoTestCase
{
+ /**
+ * Setup method
+ */
+ public function setUp(): void
+ {
+ TestLoggingUtil::getInstance()->setMockLoggingUtil();
+ }
+
public function testGetSectionObject()
{
$mockData = [
@@ -37,7 +47,7 @@ public function testGetSectionObject()
]
];
- $this->setMockParserOutput($mockData);
+ ObjectHandlerUtil::mockSectionObjectHandlerWithData($mockData);
// get sections
$sectionHandler = SectionObjectHandler::getInstance();
@@ -52,20 +62,41 @@ public function testGetSectionObject()
$this->assertNull($invalidSection);
}
+ public function testDeprecatedSection()
+ {
+ $mockData = [
+ "testSection1" => [
+ "element" => [
+ "testElement" => [
+ "type" => "input",
+ "selector" => "#element",
+ "deprecated" => "element deprecation message"
+ ]
+ ],
+ "filename" => "filename.xml",
+ "deprecated" => "section deprecation message"
+ ]
+ ];
+
+ ObjectHandlerUtil::mockSectionObjectHandlerWithData($mockData);
+
+ // get sections
+ $sectionHandler = SectionObjectHandler::getInstance();
+ $section = $sectionHandler->getObject("testSection1");
+
+ //validate deprecation warning
+ TestLoggingUtil::getInstance()->validateMockLogStatement(
+ 'notice',
+ "NOTICE: 1 Section name violations detected. See mftf.log for details.",
+ []
+ );
+ }
+
/**
- * Set the mock parser return value
- *
- * @param array $data
+ * clean up function runs after all tests
*/
- private function setMockParserOutput($data)
+ public static function tearDownAfterClass(): void
{
- // clear section object handler value to inject parsed content
- $property = new \ReflectionProperty(SectionObjectHandler::class, "INSTANCE");
- $property->setAccessible(true);
- $property->setValue(null);
-
- $mockSectionParser = AspectMock::double(SectionParser::class, ["getData" => $data])->make();
- $instance = AspectMock::double(ObjectManager::class, ["get" => $mockSectionParser])->make();
- AspectMock::double(ObjectManagerFactory::class, ["getObjectManager" => $instance]);
+ TestLoggingUtil::getInstance()->clearMockLoggingUtil();
}
}
diff --git a/dev/tests/unit/Magento/FunctionalTestFramework/StaticCheck/DeprecatedEntityUsageCheckTest.php b/dev/tests/unit/Magento/FunctionalTestFramework/StaticCheck/DeprecatedEntityUsageCheckTest.php
new file mode 100644
index 000000000..dc339c8a3
--- /dev/null
+++ b/dev/tests/unit/Magento/FunctionalTestFramework/StaticCheck/DeprecatedEntityUsageCheckTest.php
@@ -0,0 +1,251 @@
+staticCheck = new DeprecatedEntityUsageCheck();
+ $this->staticCheckClass = new \ReflectionClass($this->staticCheck);
+ }
+
+ public function tearDown(): void
+ {
+ AspectMock::clean();
+ }
+
+ public function testInvalidPathOption()
+ {
+ $input = $this->getMockBuilder(InputInterface::class)
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $input->method('getOption')
+ ->with('path')
+ ->willReturn('/invalidPath');
+
+ $loadAllXmlFiles = $this->staticCheckClass->getMethod('loadAllXMLFiles');
+ $loadAllXmlFiles->setAccessible(true);
+
+ $this->expectException(InvalidArgumentException::class);
+ $loadAllXmlFiles->invoke($this->staticCheck, $input);
+ }
+
+ public function testViolatingElementReferences()
+ {
+ //variables for assertions
+ $elementName = 'elementOne';
+ $sectionName = 'SectionOne';
+ $fileName = 'section.xml';
+
+ $element = new ElementObject($elementName, 'type', '#selector1', null, '41', false, 'deprecated');
+ $section = new SectionObject($sectionName, [$element], $fileName);
+ $elementRef = $sectionName . '.' . $elementName;
+ $references = [$elementRef => $element, $sectionName => $section];
+ $actual = $this->callViolatingReferences($references);
+ $expected = [
+ 'Deprecated Element(s)' => [
+ 0 => [
+ 'name' => $elementRef,
+ 'file' => $fileName
+ ]
+ ]
+ ];
+ $this->assertEquals($actual, $expected);
+ }
+
+ public function testViolatingPageReferences()
+ {
+ //Page variables for assertions
+ $pageName = 'Page';
+ $fileName = 'page.xml';
+
+ $page = new PageObject($pageName, '/url.html', 'Test', [], false, "test", $fileName, 'deprecated');
+ $references = ['Page' => $page];
+ $actual = $this->callViolatingReferences($references);
+ $expected = [
+ 'Deprecated Page(s)' => [
+ 0 => [
+ 'name' => $pageName,
+ 'file' => $fileName
+ ]
+ ]
+ ];
+ $this->assertEquals($actual, $expected);
+ }
+
+ public function testViolatingDataReferences()
+ {
+ //Data entity variables for assertions
+ $entityName = 'EntityOne';
+ $fileName = 'entity.xml';
+
+ $entity = new EntityDataObject(
+ $entityName,
+ 'testType',
+ ['testkey' => 'testValue'],
+ [],
+ null,
+ [],
+ null,
+ $fileName,
+ 'deprecated'
+ );
+ $references = [$entityName => $entity];
+ $actual = $this->callViolatingReferences($references);
+ $expected = [
+ 'Deprecated Data(s)' => [
+ 0 => [
+ 'name' => $entityName,
+ 'file' => $fileName
+ ]
+ ]
+ ];
+ $this->assertEquals($actual, $expected);
+ }
+
+ public function testViolatingTestReferences()
+ {
+ // test variables for assertions
+ $testName = 'Test1';
+ $fileName = 'test.xml';
+
+ $test = new TestObject($testName, [], [], [], $fileName, null, 'deprecated');
+ $references = ['Test1' => $test];
+ $actual = $this->callViolatingReferences($references);
+ $expected = [
+ 'Deprecated Test(s)' => [
+ 0 => [
+ 'name' => $testName,
+ 'file' => $fileName
+ ]
+ ]
+ ];
+ $this->assertEquals($actual, $expected);
+ }
+
+ public function testViolatingMetaDataReferences()
+ {
+ // Data Variables for Assertions
+ $dataType1 = "type1";
+ $operationType1 = "create";
+ $operationType2 = "update";
+
+ /**
+ * Parser Output.
+ * operationName
+ * createType1
+ * has field
+ * key=id, value=integer
+ * updateType1
+ * has field
+ * key=id, value=integer
+ */
+ $mockData = [OperationDefinitionObjectHandler::ENTITY_OPERATION_ROOT_TAG => [
+ "testOperationName" => [
+ OperationDefinitionObjectHandler::ENTITY_OPERATION_DATA_TYPE => $dataType1,
+ OperationDefinitionObjectHandler::ENTITY_OPERATION_TYPE => $operationType1,
+ OperationDefinitionObjectHandler::ENTITY_OPERATION_AUTH => "auth",
+ OperationDefinitionObjectHandler::ENTITY_OPERATION_URL => "V1/Type1",
+ OperationDefinitionObjectHandler::ENTITY_OPERATION_METHOD => "POST",
+ OperationDefinitionObjectHandler::ENTITY_OPERATION_ENTRY => [
+ 0 => [
+ OperationDefinitionObjectHandler::ENTITY_OPERATION_ENTRY_KEY => "id",
+ OperationDefinitionObjectHandler::ENTITY_OPERATION_ENTRY_VALUE => "integer"
+ ],
+ ],
+ OperationDefinitionObjectHandler::OBJ_DEPRECATED => 'deprecated'
+ ],[
+ OperationDefinitionObjectHandler::ENTITY_OPERATION_DATA_TYPE => $dataType1,
+ OperationDefinitionObjectHandler::ENTITY_OPERATION_TYPE => $operationType2,
+ OperationDefinitionObjectHandler::ENTITY_OPERATION_AUTH => "auth",
+ OperationDefinitionObjectHandler::ENTITY_OPERATION_URL => "V1/Type1/{id}",
+ OperationDefinitionObjectHandler::ENTITY_OPERATION_METHOD => "PUT",
+ OperationDefinitionObjectHandler::ENTITY_OPERATION_ENTRY => [
+ 0 => [
+ OperationDefinitionObjectHandler::ENTITY_OPERATION_ENTRY_KEY => "id",
+ OperationDefinitionObjectHandler::ENTITY_OPERATION_ENTRY_VALUE => "integer"
+ ],
+ ]
+ ]]];
+
+ ObjectHandlerUtil::mockOperationHandlerWithData($mockData);
+ $dataName = 'dataName1';
+ $references = [
+ $dataName => [
+ $dataType1 => [
+ $operationType1,
+ $operationType2
+ ]
+ ]
+ ];
+
+ $expected = [
+ '"'.$dataName.'" references deprecated' => [
+ 0 => [
+ 'name' => $dataType1,
+ 'file' => 'metadata xml file'
+ ]
+ ]
+ ];
+ $property = $this->staticCheckClass->getMethod('findViolatingMetadataReferences');
+ $property->setAccessible(true);
+ $actual = $property->invoke($this->staticCheck, $references);
+ $this->assertEquals($actual, $expected);
+ }
+
+ public function testIsDeprecated()
+ {
+ // Test Data
+ $contents = '
+
+
+
+
+
+ ';
+
+ $property = $this->staticCheckClass->getMethod('isDeprecated');
+ $property->setAccessible(true);
+ $output = $property->invoke($this->staticCheck, $contents);
+ $this->assertTrue($output);
+ }
+
+ /**
+ * Invoke findViolatingReferences
+ * @param $references
+ * @return mixed
+ * @throws \ReflectionException
+ */
+ public function callViolatingReferences($references)
+ {
+ $property = $this->staticCheckClass->getMethod('findViolatingReferences');
+ $property->setAccessible(true);
+ return $property->invoke($this->staticCheck, $references);
+ }
+}
diff --git a/dev/tests/unit/Magento/FunctionalTestFramework/Test/Handlers/ActionGroupObjectHandlerTest.php b/dev/tests/unit/Magento/FunctionalTestFramework/Test/Handlers/ActionGroupObjectHandlerTest.php
index deb08d783..71c1833ba 100644
--- a/dev/tests/unit/Magento/FunctionalTestFramework/Test/Handlers/ActionGroupObjectHandlerTest.php
+++ b/dev/tests/unit/Magento/FunctionalTestFramework/Test/Handlers/ActionGroupObjectHandlerTest.php
@@ -15,6 +15,7 @@
use tests\unit\Util\MagentoTestCase;
use tests\unit\Util\ActionGroupArrayBuilder;
use Magento\FunctionalTestingFramework\Test\Parsers\ActionGroupDataParser;
+use tests\unit\Util\ObjectHandlerUtil;
class ActionGroupObjectHandlerTest extends MagentoTestCase
{
@@ -34,7 +35,7 @@ public function testGetTestObjectWithInvalidExtends()
->withFilename()
->withActionObjects()
->build();
- $this->setMockParserOutput(['actionGroups' => $actionGroupOne]);
+ ObjectHandlerUtil::mockActionGroupObjectHandlerWithData(['actionGroups' => $actionGroupOne]);
$handler = ActionGroupObjectHandler::getInstance();
@@ -68,7 +69,14 @@ public function testGetAllTestObjectsWithInvalidExtends()
->withActionObjects()
->build();
- $this->setMockParserOutput(['actionGroups' => array_merge($actionGroupOne, $actionGroupTwo)]);
+ ObjectHandlerUtil::mockActionGroupObjectHandlerWithData(
+ [
+ 'actionGroups' => array_merge(
+ $actionGroupOne,
+ $actionGroupTwo
+ )
+ ]
+ );
$handler = ActionGroupObjectHandler::getInstance();
@@ -76,23 +84,4 @@ public function testGetAllTestObjectsWithInvalidExtends()
$this->expectExceptionMessage("Mftf Action Group can not extend from itself: " . $nameOne);
$handler->getAllObjects();
}
-
- /**
- * Function used to set mock for parser return and force init method to run between tests.
- *
- * @param array $data
- * @throws \Exception
- */
- private function setMockParserOutput($data)
- {
- // Clear action group object handler value to inject parsed content
- $property = new \ReflectionProperty(ActionGroupObjectHandler::class, 'instance');
- $property->setAccessible(true);
- $property->setValue(null);
-
- $mockDataParser = AspectMock::double(ActionGroupDataParser::class, ['readActionGroupData' => $data])->make();
- $instance = AspectMock::double(ObjectManager::class, ['create' => $mockDataParser])
- ->make(); // bypass the private constructor
- AspectMock::double(ObjectManagerFactory::class, ['getObjectManager' => $instance]);
- }
}
diff --git a/dev/tests/unit/Magento/FunctionalTestFramework/Test/Handlers/TestObjectHandlerTest.php b/dev/tests/unit/Magento/FunctionalTestFramework/Test/Handlers/TestObjectHandlerTest.php
index 4db2f21a4..939dd496e 100644
--- a/dev/tests/unit/Magento/FunctionalTestFramework/Test/Handlers/TestObjectHandlerTest.php
+++ b/dev/tests/unit/Magento/FunctionalTestFramework/Test/Handlers/TestObjectHandlerTest.php
@@ -17,6 +17,7 @@
use Magento\FunctionalTestingFramework\Test\Parsers\TestDataParser;
use Magento\FunctionalTestingFramework\Test\Util\TestObjectExtractor;
use tests\unit\Util\MagentoTestCase;
+use tests\unit\Util\ObjectHandlerUtil;
use tests\unit\Util\TestDataArrayBuilder;
use tests\unit\Util\MockModuleResolverBuilder;
@@ -41,7 +42,7 @@ public function testGetTestObject()
$resolverMock = new MockModuleResolverBuilder();
$resolverMock->setup();
- $this->setMockParserOutput($mockData);
+ ObjectHandlerUtil::mockTestObjectHandlerWitData($mockData);
// run object handler method
$toh = TestObjectHandler::getInstance();
@@ -78,7 +79,7 @@ public function testGetTestObject()
$expectedFailedHookObject = new TestHookObject(
TestObjectExtractor::TEST_FAILED_HOOK,
$testDataArrayBuilder->testName,
- [$expectedFailedActionObject]
+ ["saveScreenshot" => $expectedFailedActionObject]
);
$expectedTestActionObject = new ActionObject(
@@ -135,7 +136,7 @@ public function testGetTestsByGroup()
$resolverMock = new MockModuleResolverBuilder();
$resolverMock->setup();
- $this->setMockParserOutput(array_merge($includeTest, $excludeTest));
+ ObjectHandlerUtil::mockTestObjectHandlerWitData(array_merge($includeTest, $excludeTest));
// execute test method
$toh = TestObjectHandler::getInstance();
@@ -184,7 +185,7 @@ public function testGetTestWithModuleName()
$resolverMock = new MockModuleResolverBuilder();
$resolverMock->setup(['Vendor_' . $moduleExpected => $filepath]);
- $this->setMockParserOutput($mockData);
+ ObjectHandlerUtil::mockTestObjectHandlerWitData($mockData);
// Execute Test Method
$toh = TestObjectHandler::getInstance();
$actualTestObject = $toh->getObject($testDataArrayBuilder->testName);
@@ -212,7 +213,7 @@ public function testGetTestObjectWithInvalidExtends()
->build();
$resolverMock = new MockModuleResolverBuilder();
$resolverMock->setup();
- $this->setMockParserOutput($testOne);
+ ObjectHandlerUtil::mockTestObjectHandlerWitData($testOne);
$toh = TestObjectHandler::getInstance();
@@ -250,7 +251,7 @@ public function testGetAllTestObjectsWithInvalidExtends()
$resolverMock = new MockModuleResolverBuilder();
$resolverMock->setup();
- $this->setMockParserOutput(array_merge($testOne, $testTwo));
+ ObjectHandlerUtil::mockTestObjectHandlerWitData(array_merge($testOne, $testTwo));
$toh = TestObjectHandler::getInstance();
@@ -260,22 +261,96 @@ public function testGetAllTestObjectsWithInvalidExtends()
}
/**
- * Function used to set mock for parser return and force init method to run between tests.
+ * Validate test object when ENABLE_PAUSE is set to true
*
- * @param array $data
* @throws \Exception
*/
- private function setMockParserOutput($data)
+ public function testGetTestObjectWhenEnablePause()
{
- // clear test object handler value to inject parsed content
- $property = new \ReflectionProperty(TestObjectHandler::class, 'testObjectHandler');
- $property->setAccessible(true);
- $property->setValue(null);
-
- $mockDataParser = AspectMock::double(TestDataParser::class, ['readTestData' => $data])->make();
- $instance = AspectMock::double(ObjectManager::class, ['create' => $mockDataParser])
- ->make(); // bypass the private constructor
- AspectMock::double(ObjectManagerFactory::class, ['getObjectManager' => $instance]);
+ // set up mock data
+ putenv('ENABLE_PAUSE=true');
+ $testDataArrayBuilder = new TestDataArrayBuilder();
+ $mockData = $testDataArrayBuilder
+ ->withAnnotations()
+ ->withFailedHook()
+ ->withAfterHook()
+ ->withBeforeHook()
+ ->withTestActions()
+ ->build();
+
+ $resolverMock = new MockModuleResolverBuilder();
+ $resolverMock->setup();
+ ObjectHandlerUtil::mockTestObjectHandlerWitData($mockData);
+
+ // run object handler method
+ $toh = TestObjectHandler::getInstance();
+ $mockConfig = AspectMock::double(TestObjectHandler::class, ['initTestData' => false]);
+ $actualTestObject = $toh->getObject($testDataArrayBuilder->testName);
+
+ // perform asserts
+ $expectedBeforeActionObject = new ActionObject(
+ $testDataArrayBuilder->testActionBeforeName,
+ $testDataArrayBuilder->testActionType,
+ []
+ );
+ $expectedAfterActionObject = new ActionObject(
+ $testDataArrayBuilder->testActionAfterName,
+ $testDataArrayBuilder->testActionType,
+ []
+ );
+ $expectedFailedActionObject1 = new ActionObject(
+ 'saveScreenshot',
+ 'saveScreenshot',
+ []
+ );
+ $expectedFailedActionObject2 = new ActionObject(
+ 'pauseWhenFailed',
+ 'pause',
+ [ActionObject::PAUSE_ACTION_INTERNAL_ATTRIBUTE => true]
+ );
+
+ $expectedBeforeHookObject = new TestHookObject(
+ TestObjectExtractor::TEST_BEFORE_HOOK,
+ $testDataArrayBuilder->testName,
+ ["testActionBefore" => $expectedBeforeActionObject]
+ );
+ $expectedAfterHookObject = new TestHookObject(
+ TestObjectExtractor::TEST_AFTER_HOOK,
+ $testDataArrayBuilder->testName,
+ ["testActionAfter" => $expectedAfterActionObject]
+ );
+ $expectedFailedHookObject = new TestHookObject(
+ TestObjectExtractor::TEST_FAILED_HOOK,
+ $testDataArrayBuilder->testName,
+ [
+ "saveScreenshot" => $expectedFailedActionObject1,
+ "pauseWhenFailed" => $expectedFailedActionObject2,
+ ]
+ );
+
+ $expectedTestActionObject = new ActionObject(
+ $testDataArrayBuilder->testTestActionName,
+ $testDataArrayBuilder->testActionType,
+ []
+ );
+ $expectedTestObject = new TestObject(
+ $testDataArrayBuilder->testName,
+ ["testActionInTest" => $expectedTestActionObject],
+ [
+ 'features' => ['NO MODULE DETECTED'],
+ 'group' => ['test'],
+ 'description' => ['test_files' => '
Test files
', 'deprecated' => []]
+ ],
+ [
+ TestObjectExtractor::TEST_BEFORE_HOOK => $expectedBeforeHookObject,
+ TestObjectExtractor::TEST_AFTER_HOOK => $expectedAfterHookObject,
+ TestObjectExtractor::TEST_FAILED_HOOK => $expectedFailedHookObject
+ ],
+ null
+ );
+
+ $this->assertEquals($expectedTestObject, $actualTestObject);
+ putenv('ENABLE_PAUSE');
}
/**
diff --git a/dev/tests/unit/Magento/FunctionalTestFramework/Test/Util/ActionGroupObjectExtractorTest.php b/dev/tests/unit/Magento/FunctionalTestFramework/Test/Util/ActionGroupObjectExtractorTest.php
index 2c9e171d5..1fea7e848 100644
--- a/dev/tests/unit/Magento/FunctionalTestFramework/Test/Util/ActionGroupObjectExtractorTest.php
+++ b/dev/tests/unit/Magento/FunctionalTestFramework/Test/Util/ActionGroupObjectExtractorTest.php
@@ -34,23 +34,47 @@ public function testEmptyStepKey()
$this->testActionGroupObjectExtractor->extractActionGroup($this->createBasicActionObjectArray(""));
}
+ /**
+ * Tests deprecation message for an action group
+ */
+ public function testDeprecationMessage()
+ {
+ $this->testActionGroupObjectExtractor->extractActionGroup(
+ $this->createBasicActionObjectArray(
+ "testDeprecatedAction1",
+ "actionGroup",
+ "filename1.xml",
+ "message"
+ )
+ );
+
+ TestLoggingUtil::getInstance()->validateMockLogStatement(
+ 'warning',
+ "DEPRECATION: The action group 'actionGroup' is deprecated.",
+ ["fileName" => "filename1.xml", "deprecatedMessage" => "message"]
+ );
+ }
+
/**
* Utility function to return mock parser output for testing extraction into ActionObjects.
*
* @param string $stepKey
* @param string $actionGroup
* @param string $filename
+ * @param string $deprecated
* @return array
*/
private function createBasicActionObjectArray(
$stepKey = 'testAction1',
$actionGroup = "actionGroup",
- $filename = "filename.xml"
+ $filename = "filename.xml",
+ $deprecated = null
) {
$baseArray = [
'nodeName' => 'actionGroup',
'name' => $actionGroup,
'filename' => $filename,
+ 'deprecated' => $deprecated,
$stepKey => [
"nodeName" => "sampleAction",
"stepKey" => $stepKey,
diff --git a/dev/tests/unit/Util/ObjectHandlerUtil.php b/dev/tests/unit/Util/ObjectHandlerUtil.php
new file mode 100644
index 000000000..0e4543f2b
--- /dev/null
+++ b/dev/tests/unit/Util/ObjectHandlerUtil.php
@@ -0,0 +1,145 @@
+setAccessible(true);
+ $property->setValue(null);
+
+ $mockOperationParser = AspectMock::double(
+ OperationDefinitionParser::class,
+ ["readOperationMetadata" => $data]
+ )->make();
+ $instance = AspectMock::double(ObjectManager::class, ['create' => $mockOperationParser])->make();
+ AspectMock::double(ObjectManagerFactory::class, ['getObjectManager' => $instance]);
+ }
+
+ /**
+ * Set up everything required to mock DataObjectHandler::getInstance() with $data value
+ *
+ * @param array $data
+ */
+ public static function mockDataObjectHandlerWithData($data)
+ {
+ // Clear DataObjectHandler singleton if already set
+ $property = new \ReflectionProperty(DataObjectHandler::class, "INSTANCE");
+ $property->setAccessible(true);
+ $property->setValue(null);
+
+ $mockDataProfileSchemaParser = AspectMock::double(DataProfileSchemaParser::class, [
+ 'readDataProfiles' => $data
+ ])->make();
+
+ $mockObjectManager = AspectMock::double(ObjectManager::class, [
+ 'create' => $mockDataProfileSchemaParser
+ ])->make();
+
+ AspectMock::double(ObjectManagerFactory::class, [
+ 'getObjectManager' => $mockObjectManager
+ ]);
+ }
+
+ /**
+ * Set up everything required to mock PageObjectHandler::getInstance() with $data value
+ *
+ * @param array $data
+ */
+ public static function mockPageObjectHandlerWithData($data)
+ {
+ // clear section object handler value to inject parsed content
+ $property = new \ReflectionProperty(PageObjectHandler::class, 'INSTANCE');
+ $property->setAccessible(true);
+ $property->setValue(null);
+
+ $mockSectionParser = AspectMock::double(PageParser::class, ["getData" => $data])->make();
+ $instance = AspectMock::double(ObjectManager::class, ['get' => $mockSectionParser])->make();
+ AspectMock::double(ObjectManagerFactory::class, ['getObjectManager' => $instance]);
+ }
+
+ /**
+ * Set up everything required to mock SectionObjectHandler::getInstance() with $data value
+ *
+ * @param array $data
+ */
+ public static function mockSectionObjectHandlerWithData($data)
+ {
+ // clear section object handler value to inject parsed content
+ $property = new \ReflectionProperty(SectionObjectHandler::class, "INSTANCE");
+ $property->setAccessible(true);
+ $property->setValue(null);
+
+ $mockSectionParser = AspectMock::double(SectionParser::class, ["getData" => $data])->make();
+ $instance = AspectMock::double(ObjectManager::class, ["get" => $mockSectionParser])->make();
+ AspectMock::double(ObjectManagerFactory::class, ["getObjectManager" => $instance]);
+ }
+
+ /**
+ * Set up everything required to mock TestObjectHandler::getInstance() with $data value
+ *
+ * @param array $data
+ * @throws \Exception
+ */
+ public static function mockTestObjectHandlerWitData($data)
+ {
+ // clear test object handler value to inject parsed content
+ $property = new \ReflectionProperty(TestObjectHandler::class, 'testObjectHandler');
+ $property->setAccessible(true);
+ $property->setValue(null);
+
+ $mockDataParser = AspectMock::double(TestDataParser::class, ['readTestData' => $data])->make();
+ $instance = AspectMock::double(ObjectManager::class, ['create' => $mockDataParser])
+ ->make(); // bypass the private constructor
+ AspectMock::double(ObjectManagerFactory::class, ['getObjectManager' => $instance]);
+ }
+
+ /**
+ * Set up everything required to mock ActionGroupObjectHandler::getInstance() with $data value
+ *
+ * @param array $data
+ * @throws \Exception
+ */
+ public static function mockActionGroupObjectHandlerWithData($data)
+ {
+ // Clear action group object handler value to inject parsed content
+ $property = new \ReflectionProperty(ActionGroupObjectHandler::class, 'instance');
+ $property->setAccessible(true);
+ $property->setValue(null);
+
+ $mockDataParser = AspectMock::double(ActionGroupDataParser::class, ['readActionGroupData' => $data])->make();
+ $instance = AspectMock::double(ObjectManager::class, ['create' => $mockDataParser])
+ ->make(); // bypass the private constructor
+ AspectMock::double(ObjectManagerFactory::class, ['getObjectManager' => $instance]);
+ }
+}
diff --git a/dev/tests/util/MftfStaticTestCase.php b/dev/tests/util/MftfStaticTestCase.php
new file mode 100644
index 000000000..708561031
--- /dev/null
+++ b/dev/tests/util/MftfStaticTestCase.php
@@ -0,0 +1,49 @@
+getMockBuilder(InputInterface::class)
+ ->disableOriginalConstructor()
+ ->getMock();
+ if ($path) {
+ $input->method('getOption')
+ ->with('path')
+ ->willReturn($path);
+ }
+ return $input;
+ }
+
+ public function tearDown(): void
+ {
+ DirSetupUtil::rmdirRecursive(self::STATIC_RESULTS_DIR);
+ }
+}
diff --git a/dev/tests/verification/DeprecationCheckModule/ActionGroup/DeprecationCheckActionGroup.xml b/dev/tests/verification/DeprecationCheckModule/ActionGroup/DeprecationCheckActionGroup.xml
new file mode 100644
index 000000000..c83dab211
--- /dev/null
+++ b/dev/tests/verification/DeprecationCheckModule/ActionGroup/DeprecationCheckActionGroup.xml
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
diff --git a/dev/tests/verification/DeprecationCheckModule/ActionGroup/DeprecationCheckDeprecatedActionGroup.xml b/dev/tests/verification/DeprecationCheckModule/ActionGroup/DeprecationCheckDeprecatedActionGroup.xml
new file mode 100644
index 000000000..e8412a29e
--- /dev/null
+++ b/dev/tests/verification/DeprecationCheckModule/ActionGroup/DeprecationCheckDeprecatedActionGroup.xml
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
diff --git a/dev/tests/verification/DeprecationCheckModule/Data/DeprecationCheckData.xml b/dev/tests/verification/DeprecationCheckModule/Data/DeprecationCheckData.xml
new file mode 100644
index 000000000..75a6b6678
--- /dev/null
+++ b/dev/tests/verification/DeprecationCheckModule/Data/DeprecationCheckData.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
+ value
+
+
diff --git a/dev/tests/verification/DeprecationCheckModule/Metadata/DeprecationCheckMeta.xml b/dev/tests/verification/DeprecationCheckModule/Metadata/DeprecationCheckMeta.xml
new file mode 100644
index 000000000..757c6284b
--- /dev/null
+++ b/dev/tests/verification/DeprecationCheckModule/Metadata/DeprecationCheckMeta.xml
@@ -0,0 +1,18 @@
+
+
+
+
+
+ application/json
+
+
+
\ No newline at end of file
diff --git a/dev/tests/verification/DeprecationCheckModule/Page/DeprecationCheckPage.xml b/dev/tests/verification/DeprecationCheckModule/Page/DeprecationCheckPage.xml
new file mode 100644
index 000000000..dd3f187dc
--- /dev/null
+++ b/dev/tests/verification/DeprecationCheckModule/Page/DeprecationCheckPage.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
diff --git a/dev/tests/verification/DeprecationCheckModule/Section/DeprecationCheckSection.xml b/dev/tests/verification/DeprecationCheckModule/Section/DeprecationCheckSection.xml
new file mode 100644
index 000000000..1837e1ef5
--- /dev/null
+++ b/dev/tests/verification/DeprecationCheckModule/Section/DeprecationCheckSection.xml
@@ -0,0 +1,16 @@
+
+
+
+
+
+
diff --git a/dev/tests/verification/DeprecationCheckModule/Suite/deprecationCheckSuite.xml b/dev/tests/verification/DeprecationCheckModule/Suite/deprecationCheckSuite.xml
new file mode 100644
index 000000000..b89ad99f6
--- /dev/null
+++ b/dev/tests/verification/DeprecationCheckModule/Suite/deprecationCheckSuite.xml
@@ -0,0 +1,16 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/dev/tests/verification/DeprecationCheckModule/Test/DeprecationCheckDeprecatedTest.xml b/dev/tests/verification/DeprecationCheckModule/Test/DeprecationCheckDeprecatedTest.xml
new file mode 100644
index 000000000..1b3ace053
--- /dev/null
+++ b/dev/tests/verification/DeprecationCheckModule/Test/DeprecationCheckDeprecatedTest.xml
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/dev/tests/verification/DeprecationCheckModule/Test/DeprecationCheckTest.xml b/dev/tests/verification/DeprecationCheckModule/Test/DeprecationCheckTest.xml
new file mode 100644
index 000000000..1c4fc3dba
--- /dev/null
+++ b/dev/tests/verification/DeprecationCheckModule/Test/DeprecationCheckTest.xml
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/dev/tests/verification/PauseCheckModule/ActionGroup/ActionGroupWithMultiplePausesActionGroup.xml b/dev/tests/verification/PauseCheckModule/ActionGroup/ActionGroupWithMultiplePausesActionGroup.xml
new file mode 100644
index 000000000..5e9299631
--- /dev/null
+++ b/dev/tests/verification/PauseCheckModule/ActionGroup/ActionGroupWithMultiplePausesActionGroup.xml
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/dev/tests/verification/PauseCheckModule/ActionGroup/ActionGroupWithNoPauseActionGroup.xml b/dev/tests/verification/PauseCheckModule/ActionGroup/ActionGroupWithNoPauseActionGroup.xml
new file mode 100644
index 000000000..8acae47e8
--- /dev/null
+++ b/dev/tests/verification/PauseCheckModule/ActionGroup/ActionGroupWithNoPauseActionGroup.xml
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+
+
+
diff --git a/dev/tests/verification/PauseCheckModule/ActionGroup/ActionGroupWithPauseActionGroup.xml b/dev/tests/verification/PauseCheckModule/ActionGroup/ActionGroupWithPauseActionGroup.xml
new file mode 100644
index 000000000..aaef1befa
--- /dev/null
+++ b/dev/tests/verification/PauseCheckModule/ActionGroup/ActionGroupWithPauseActionGroup.xml
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+
+
+
diff --git a/dev/tests/verification/PauseCheckModule/Suite/suiteWithMultiplePauseActionsSuite.xml b/dev/tests/verification/PauseCheckModule/Suite/suiteWithMultiplePauseActionsSuite.xml
new file mode 100644
index 000000000..47ff1088b
--- /dev/null
+++ b/dev/tests/verification/PauseCheckModule/Suite/suiteWithMultiplePauseActionsSuite.xml
@@ -0,0 +1,28 @@
+
+
+
+
+
+
+
+
+
+
+
+ dataHere
+
+
+
+
+
+
+
+
+
+
+
diff --git a/dev/tests/verification/PauseCheckModule/Suite/suiteWithPauseActionSuite.xml b/dev/tests/verification/PauseCheckModule/Suite/suiteWithPauseActionSuite.xml
new file mode 100644
index 000000000..71a3b5769
--- /dev/null
+++ b/dev/tests/verification/PauseCheckModule/Suite/suiteWithPauseActionSuite.xml
@@ -0,0 +1,27 @@
+
+
+
+
+
+
+
+
+
+
+
+ dataHere
+
+
+
+
+
+
+
+
+
+
diff --git a/dev/tests/verification/PauseCheckModule/Test/TestWithMultiplePauseActionsTest.xml b/dev/tests/verification/PauseCheckModule/Test/TestWithMultiplePauseActionsTest.xml
new file mode 100644
index 000000000..fa47e976c
--- /dev/null
+++ b/dev/tests/verification/PauseCheckModule/Test/TestWithMultiplePauseActionsTest.xml
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/dev/tests/verification/PauseCheckModule/Test/TestWithPauseActionTest.xml b/dev/tests/verification/PauseCheckModule/Test/TestWithPauseActionTest.xml
new file mode 100644
index 000000000..70d0903b8
--- /dev/null
+++ b/dev/tests/verification/PauseCheckModule/Test/TestWithPauseActionTest.xml
@@ -0,0 +1,23 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/dev/tests/verification/Resources/ActionGroupReturningValueTest.txt b/dev/tests/verification/Resources/ActionGroupReturningValueTest.txt
new file mode 100644
index 000000000..08e0b44f8
--- /dev/null
+++ b/dev/tests/verification/Resources/ActionGroupReturningValueTest.txt
@@ -0,0 +1,75 @@
+Test filesverification/TestModule/Test/ActionGroupFunctionalTest/ActionGroupReturningValueTest.xml
")
+ */
+class ActionGroupReturningValueTestCest
+{
+ /**
+ * @param AcceptanceTester $I
+ * @throws \Exception
+ */
+ public function _before(AcceptanceTester $I)
+ {
+ $I->createEntity("createPersonParam", "hook", "ReplacementPerson", [], []); // stepKey: createPersonParam
+ $I->comment("Entering Action Group [beforeGroup] FunctionalActionGroup");
+ $I->fillField("#foo", "myData1"); // stepKey: fillField1BeforeGroup
+ $I->fillField("#bar", "myData2"); // stepKey: fillField2BeforeGroup
+ $I->comment("Exiting Action Group [beforeGroup] FunctionalActionGroup");
+ }
+
+ /**
+ * @param AcceptanceTester $I
+ * @throws \Exception
+ */
+ public function _after(AcceptanceTester $I)
+ {
+ $I->comment("Entering Action Group [afterGroup] FunctionalActionGroup");
+ $I->fillField("#foo", "myData1"); // stepKey: fillField1AfterGroup
+ $I->fillField("#bar", "myData2"); // stepKey: fillField2AfterGroup
+ $I->comment("Exiting Action Group [afterGroup] FunctionalActionGroup");
+ }
+
+ /**
+ * @param AcceptanceTester $I
+ * @throws \Exception
+ */
+ public function _failed(AcceptanceTester $I)
+ {
+ $I->saveScreenshot(); // stepKey: saveScreenshot
+ }
+
+ /**
+ * @Severity(level = SeverityLevel::CRITICAL)
+ * @Features({"TestModule"})
+ * @Stories({"MQE-433"})
+ * @Parameter(name = "AcceptanceTester", value="$I")
+ * @param AcceptanceTester $I
+ * @return void
+ * @throws \Exception
+ */
+ public function ActionGroupReturningValueTest(AcceptanceTester $I)
+ {
+ $I->amOnPage("/someUrl"); // stepKey: step1
+ $I->comment("Entering Action Group [actionGroupWithReturnValue1] FunctionalActionGroupWithReturnValueActionGroup");
+ $grabTextFrom1ActionGroupWithReturnValue1 = $I->grabTextFrom("#foo"); // stepKey: grabTextFrom1ActionGroupWithReturnValue1
+ $actionGroupWithReturnValue1 = $I->return($grabTextFrom1ActionGroupWithReturnValue1); // stepKey: returnActionGroupWithReturnValue1
+ $I->comment("Exiting Action Group [actionGroupWithReturnValue1] FunctionalActionGroupWithReturnValueActionGroup");
+ $I->comment("Entering Action Group [actionGroupWithStringUsage1] actionGroupWithStringUsage");
+ $I->see($actionGroupWithReturnValue1, "#element .{$actionGroupWithReturnValue1}"); // stepKey: see1ActionGroupWithStringUsage1
+ $I->comment("Exiting Action Group [actionGroupWithStringUsage1] actionGroupWithStringUsage");
+ }
+}
diff --git a/dev/tests/verification/Resources/ActionsInDifferentModulesSuite.txt b/dev/tests/verification/Resources/ActionsInDifferentModulesSuite.txt
new file mode 100644
index 000000000..d4e1b6eb3
--- /dev/null
+++ b/dev/tests/verification/Resources/ActionsInDifferentModulesSuite.txt
@@ -0,0 +1,218 @@
+webDriver = $this->getModule('\Magento\FunctionalTestingFramework\Module\MagentoWebDriver');
+ $this->moduleContainer = $this->webDriver->getModuleContainer();
+ // increment test count per execution
+ $this->currentTestRun++;
+ $this->executePreConditions();
+
+ if ($this->preconditionFailure != null) {
+ //if our preconditions fail, we need to mark all the tests as incomplete.
+ $e->getTest()->getMetadata()->setIncomplete("SUITE PRECONDITION FAILED:" . PHP_EOL . $this->preconditionFailure);
+ }
+ }
+
+ private function executePreConditions()
+ {
+ if ($this->currentTestRun == 1) {
+ print sprintf(self::$HOOK_EXECUTION_INIT, "before");
+
+ try {
+ if ($this->webDriver->webDriver != null) {
+ $this->webDriver->_restart();
+ } else {
+ $this->webDriver->_initializeSession();
+ }
+ $cli = $this->getModuleForAction("magentoCLISecret")->magentoCLISecret($this->getModuleForAction("getSecret")->getSecret("magento/some/secret"), 60); // stepKey: cli
+ print($cli); // stepKey: cli
+ $create1Fields['someKey'] = "dataHere";
+ PersistedObjectHandler::getInstance()->createEntity(
+ "create1",
+ "suite",
+ "SecretData",
+ [],
+ $create1Fields
+ );
+ PersistedObjectHandler::getInstance()->createEntity(
+ "create2",
+ "suite",
+ "SecretData",
+ []
+ );
+ PersistedObjectHandler::getInstance()->createEntity(
+ "create3",
+ "suite",
+ "SecretData",
+ ["create1", "create2"]
+ );
+ $this->getModuleForAction("fillSecretField")->fillSecretField("#fill", $this->getModuleForAction("getSecret")->getSecret("magento/some/secret") . "+" . $this->getModuleForAction("getSecret")->getSecret("magento/some/secret")); // stepKey: fillBefore
+ $this->getModuleForAction("click")->click(PersistedObjectHandler::getInstance()->retrieveEntityField('create2', 'key2', 'suite')); // stepKey: click
+ print("Entering Action Group [return1] ActionGroupReturningValueActionGroup");
+ $grabProducts1Return1 = $this->getModuleForAction("grabMultiple")->grabMultiple("selector"); // stepKey: grabProducts1Return1
+ $this->getModuleForAction("assertCount")->assertCount(1, $grabProducts1Return1); // stepKey: assertCountReturn1
+ $return1 = $this->getModuleForAction("return")->return($grabProducts1Return1); // stepKey: returnProducts1Return1
+ print("Exiting Action Group [return1] ActionGroupReturningValueActionGroup");
+ } catch (\Exception $exception) {
+ $this->preconditionFailure = $exception->getMessage();
+ }
+
+ // reset configuration and close session
+ $this->webDriver->_resetConfig();
+ $this->webDriver->webDriver->close();
+ $this->webDriver->webDriver = null;
+
+ print sprintf(self::$HOOK_EXECUTION_END, "before");
+ }
+ }
+
+ public function _after(\Codeception\Event\TestEvent $e)
+ {
+ $this->executePostConditions($e);
+ }
+
+ private function executePostConditions(\Codeception\Event\TestEvent $e)
+ {
+ if ($this->currentTestRun == $this->testCount) {
+ print sprintf(self::$HOOK_EXECUTION_INIT, "after");
+
+ try {
+ // Find out if Test in Suite failed, will cause potential failures in suite after
+ $cest = $e->getTest();
+
+ //Access private TestResultObject to find stack and if there are any errors (as opposed to failures)
+ $testResultObject = call_user_func(\Closure::bind(
+ function () use ($cest) {
+ return $cest->getTestResultObject();
+ },
+ $cest
+ ));
+ $errors = $testResultObject->errors();
+
+ if (!empty($errors)) {
+ foreach ($errors as $error) {
+ if ($error->failedTest()->getTestMethod() == $cest->getName()) {
+ // Do not attempt to run _after if failure was in the _after block
+ // Try to run _after but catch exceptions to prevent them from overwriting original failure.
+ print("LAST TEST IN SUITE FAILED, TEST AFTER MAY NOT BE SUCCESSFUL\n");
+ }
+ }
+ }
+ if ($this->webDriver->webDriver != null) {
+ $this->webDriver->_restart();
+ } else {
+ $this->webDriver->_initializeSession();
+ }
+ print("Entering Action Group [return2] ExtendedActionGroupReturningValueActionGroup");
+ $grabProducts1Return2 = $this->getModuleForAction("grabMultiple")->grabMultiple("selector"); // stepKey: grabProducts1Return2
+ $this->getModuleForAction("assertCount")->assertCount(1, $grabProducts1Return2); // stepKey: assertCountReturn2
+ $return2 = $this->getModuleForAction("return")->return($grabProducts1Return2); // stepKey: returnProducts1Return2
+ $grabProducts2Return2 = $this->getModuleForAction("grabMultiple")->grabMultiple("otherSelector"); // stepKey: grabProducts2Return2
+ $this->getModuleForAction("assertCount")->assertCount(2, $grabProducts2Return2); // stepKey: assertSecondCountReturn2
+ $return2 = $this->getModuleForAction("return")->return($grabProducts2Return2); // stepKey: returnProducts2Return2
+ print("Exiting Action Group [return2] ExtendedActionGroupReturningValueActionGroup");
+ PersistedObjectHandler::getInstance()->deleteEntity(
+ "create1",
+ "suite"
+ );
+ PersistedObjectHandler::getInstance()->deleteEntity(
+ "create2",
+ "suite"
+ );
+ PersistedObjectHandler::getInstance()->deleteEntity(
+ "create3",
+ "suite"
+ );
+ $this->getModuleForAction("deleteEntityByUrl")->deleteEntityByUrl("deleteThis"); // stepKey: deleteThis
+ $this->getModuleForAction("fillSecretField")->fillSecretField("#fill", $this->getModuleForAction("getSecret")->getSecret("magento/some/secret")); // stepKey: fillAfter
+ $cli2 = $this->getModuleForAction("magentoCLISecret")->magentoCLISecret($this->getModuleForAction("getSecret")->getSecret("magento/some/secret") . "-some/data-" . $this->getModuleForAction("getSecret")->getSecret("magento/some/secret"), 60); // stepKey: cli2
+ print($cli2); // stepKey: cli2
+ } catch (\Exception $exception) {
+ print $exception->getMessage();
+ }
+
+ PersistedObjectHandler::getInstance()->clearSuiteObjects();
+
+ $this->closeSession($this->webDriver);
+
+ print sprintf(self::$HOOK_EXECUTION_END, "after");
+ }
+ }
+
+ /**
+ * Close session method closes current session.
+ * If config 'close_all_sessions' is set to 'true' all sessions will be closed.
+ *
+ * return void
+ */
+ private function closeSession(): void
+ {
+ $webDriverConfig = $this->webDriver->_getConfig();
+ $this->webDriver->_closeSession();
+ if (isset($webDriverConfig['close_all_sessions']) && $webDriverConfig['close_all_sessions'] === "true") {
+ $wdHost = sprintf(
+ '%s://%s:%s%s',
+ $webDriverConfig['protocol'],
+ $webDriverConfig['host'],
+ $webDriverConfig['port'],
+ $webDriverConfig['path']
+ );
+ $availableSessions = RemoteWebDriver::getAllSessions($wdHost);
+ foreach ($availableSessions as $session) {
+ $remoteWebDriver = RemoteWebDriver::createBySessionID($session['id'], $wdHost);
+ $remoteWebDriver->quit();
+ }
+ }
+ }
+
+ /**
+ * Return the module for an action.
+ *
+ * @param string $action
+ * @return Module
+ * @throws \Exception
+ */
+ private function getModuleForAction($action)
+ {
+ $module = $this->moduleContainer->moduleForAction($action);
+ if ($module === null) {
+ throw new TestFrameworkException('Invalid action "' . $action . '"' . PHP_EOL);
+ }
+ return $module;
+ }
+}
diff --git a/dev/tests/verification/Resources/ExtendedActionGroupReturningValueTest.txt b/dev/tests/verification/Resources/ExtendedActionGroupReturningValueTest.txt
new file mode 100644
index 000000000..2f26a6463
--- /dev/null
+++ b/dev/tests/verification/Resources/ExtendedActionGroupReturningValueTest.txt
@@ -0,0 +1,40 @@
+Test filesverification/TestModule/Test/ActionGroupTest/ExtendedActionGroupReturningValueTest.xml
")
+ */
+class ExtendedActionGroupReturningValueTestCest
+{
+ /**
+ * @Severity(level = SeverityLevel::CRITICAL)
+ * @Features({"TestModule"})
+ * @Parameter(name = "AcceptanceTester", value="$I")
+ * @param AcceptanceTester $I
+ * @return void
+ * @throws \Exception
+ */
+ public function ExtendedActionGroupReturningValueTest(AcceptanceTester $I)
+ {
+ $I->comment("Entering Action Group [actionGroupReturningValue] ActionGroupReturningValueActionGroup");
+ $grabProducts1ActionGroupReturningValue = $I->grabMultiple("selector"); // stepKey: grabProducts1ActionGroupReturningValue
+ $I->assertCount(99, $grabProducts1ActionGroupReturningValue); // stepKey: assertCountActionGroupReturningValue
+ $actionGroupReturningValue = $I->return($grabProducts1ActionGroupReturningValue); // stepKey: returnProducts1ActionGroupReturningValue
+ $I->comment("Exiting Action Group [actionGroupReturningValue] ActionGroupReturningValueActionGroup");
+ $I->comment("Entering Action Group [actionGroupWithStringUsage1] actionGroupWithStringUsage");
+ $I->see($actionGroupReturningValue, "#element .{$actionGroupReturningValue}"); // stepKey: see1ActionGroupWithStringUsage1
+ $I->comment("Exiting Action Group [actionGroupWithStringUsage1] actionGroupWithStringUsage");
+ }
+}
diff --git a/dev/tests/verification/Resources/ExtendedChildActionGroupReturningValueTest.txt b/dev/tests/verification/Resources/ExtendedChildActionGroupReturningValueTest.txt
new file mode 100644
index 000000000..d043a5c04
--- /dev/null
+++ b/dev/tests/verification/Resources/ExtendedChildActionGroupReturningValueTest.txt
@@ -0,0 +1,43 @@
+Test filesverification/TestModule/Test/ActionGroupTest/ExtendedChildActionGroupReturningValueTest.xml
")
+ */
+class ExtendedChildActionGroupReturningValueTestCest
+{
+ /**
+ * @Severity(level = SeverityLevel::CRITICAL)
+ * @Features({"TestModule"})
+ * @Parameter(name = "AcceptanceTester", value="$I")
+ * @param AcceptanceTester $I
+ * @return void
+ * @throws \Exception
+ */
+ public function ExtendedChildActionGroupReturningValueTest(AcceptanceTester $I)
+ {
+ $I->comment("Entering Action Group [extendedActionGroupReturningValue] ExtendedActionGroupReturningValueActionGroup");
+ $grabProducts1ExtendedActionGroupReturningValue = $I->grabMultiple("selector"); // stepKey: grabProducts1ExtendedActionGroupReturningValue
+ $I->assertCount(99, $grabProducts1ExtendedActionGroupReturningValue); // stepKey: assertCountExtendedActionGroupReturningValue
+ $extendedActionGroupReturningValue = $I->return($grabProducts1ExtendedActionGroupReturningValue); // stepKey: returnProducts1ExtendedActionGroupReturningValue
+ $grabProducts2ExtendedActionGroupReturningValue = $I->grabMultiple("otherSelector"); // stepKey: grabProducts2ExtendedActionGroupReturningValue
+ $I->assertCount(8000, $grabProducts2ExtendedActionGroupReturningValue); // stepKey: assertSecondCountExtendedActionGroupReturningValue
+ $extendedActionGroupReturningValue = $I->return($grabProducts2ExtendedActionGroupReturningValue); // stepKey: returnProducts2ExtendedActionGroupReturningValue
+ $I->comment("Exiting Action Group [extendedActionGroupReturningValue] ExtendedActionGroupReturningValueActionGroup");
+ $I->comment("Entering Action Group [actionGroupWithStringUsage1] actionGroupWithStringUsage");
+ $I->see($extendedActionGroupReturningValue, "#element .{$extendedActionGroupReturningValue}"); // stepKey: see1ActionGroupWithStringUsage1
+ $I->comment("Exiting Action Group [actionGroupWithStringUsage1] actionGroupWithStringUsage");
+ }
+}
diff --git a/dev/tests/verification/Resources/MergedActionGroupReturningValueTest.txt b/dev/tests/verification/Resources/MergedActionGroupReturningValueTest.txt
new file mode 100644
index 000000000..a4689779b
--- /dev/null
+++ b/dev/tests/verification/Resources/MergedActionGroupReturningValueTest.txt
@@ -0,0 +1,77 @@
+Test filesverification/TestModule/Test/MergeFunctionalTest/MergedActionGroupReturningValueTest.xml
")
+ */
+class MergedActionGroupReturningValueTestCest
+{
+ /**
+ * @param AcceptanceTester $I
+ * @throws \Exception
+ */
+ public function _before(AcceptanceTester $I)
+ {
+ $I->createEntity("createPersonParam", "hook", "ReplacementPerson", [], []); // stepKey: createPersonParam
+ $I->comment("Entering Action Group [beforeGroup] FunctionalActionGroup");
+ $I->fillField("#foo", "myData1"); // stepKey: fillField1BeforeGroup
+ $I->fillField("#bar", "myData2"); // stepKey: fillField2BeforeGroup
+ $I->comment("Exiting Action Group [beforeGroup] FunctionalActionGroup");
+ }
+
+ /**
+ * @param AcceptanceTester $I
+ * @throws \Exception
+ */
+ public function _after(AcceptanceTester $I)
+ {
+ $I->comment("Entering Action Group [afterGroup] FunctionalActionGroup");
+ $I->fillField("#foo", "myData1"); // stepKey: fillField1AfterGroup
+ $I->fillField("#bar", "myData2"); // stepKey: fillField2AfterGroup
+ $I->comment("Exiting Action Group [afterGroup] FunctionalActionGroup");
+ }
+
+ /**
+ * @param AcceptanceTester $I
+ * @throws \Exception
+ */
+ public function _failed(AcceptanceTester $I)
+ {
+ $I->saveScreenshot(); // stepKey: saveScreenshot
+ }
+
+ /**
+ * @Severity(level = SeverityLevel::CRITICAL)
+ * @Features({"TestModule"})
+ * @Stories({"MQE-433"})
+ * @Parameter(name = "AcceptanceTester", value="$I")
+ * @param AcceptanceTester $I
+ * @return void
+ * @throws \Exception
+ */
+ public function MergedActionGroupReturningValueTest(AcceptanceTester $I)
+ {
+ $I->amOnPage("/someUrl"); // stepKey: step1
+ $I->comment("Entering Action Group [actionGroupWithReturnValue1] MergeActionGroupReturningValueActionGroup");
+ $I->amOnPage("/Jane/Dane.html"); // stepKey: amOnPage1ActionGroupWithReturnValue1
+ $I->click(".merge .Jane"); // stepKey: myMergedClickActionGroupWithReturnValue1
+ $grabMultiple1ActionGroupWithReturnValue1 = $I->grabMultiple("#foo"); // stepKey: grabMultiple1ActionGroupWithReturnValue1
+ $actionGroupWithReturnValue1 = $I->return($grabMultiple1ActionGroupWithReturnValue1); // stepKey: returnValueActionGroupWithReturnValue1
+ $I->comment("Exiting Action Group [actionGroupWithReturnValue1] MergeActionGroupReturningValueActionGroup");
+ $I->comment("Entering Action Group [actionGroupWithStringUsage1] actionGroupWithStringUsage");
+ $I->see($actionGroupWithReturnValue1, "#element .{$actionGroupWithReturnValue1}"); // stepKey: see1ActionGroupWithStringUsage1
+ $I->comment("Exiting Action Group [actionGroupWithStringUsage1] actionGroupWithStringUsage");
+ }
+}
diff --git a/dev/tests/verification/Resources/PageReplacementTest.txt b/dev/tests/verification/Resources/PageReplacementTest.txt
index cda119999..6f75ee096 100644
--- a/dev/tests/verification/Resources/PageReplacementTest.txt
+++ b/dev/tests/verification/Resources/PageReplacementTest.txt
@@ -35,8 +35,8 @@ class PageReplacementTestCest
$I->amOnPage("/John/StringLiteral2.html"); // stepKey: twoParamPageStringData
$I->amOnPage("/John/" . $I->retrieveEntityField('datakey', 'firstname', 'test') . ".html"); // stepKey: twoParamPageDataPersist
$I->amOnPage("/" . $I->retrieveEntityField('datakey', 'firstname', 'test') . "/StringLiteral2.html"); // stepKey: twoParamPagePersistString
- $I->amOnPage("/" . getenv("MAGENTO_BACKEND_NAME") . "/backend"); // stepKey: onAdminPage
- $I->amOnPage("/" . getenv("MAGENTO_BACKEND_NAME") . "/StringLiteral/page.html"); // stepKey: oneParamAdminPageString
+ $I->amOnPage((getenv("MAGENTO_BACKEND_BASE_URL") ? rtrim(getenv("MAGENTO_BACKEND_BASE_URL"), "/") : "") . "/" . getenv("MAGENTO_BACKEND_NAME") . "/backend"); // stepKey: onAdminPage
+ $I->amOnPage((getenv("MAGENTO_BACKEND_BASE_URL") ? rtrim(getenv("MAGENTO_BACKEND_BASE_URL"), "/") : "") . "/" . getenv("MAGENTO_BACKEND_NAME") . "/StringLiteral/page.html"); // stepKey: oneParamAdminPageString
$I->amOnUrl("http://myFullUrl.com/"); // stepKey: onExternalPage
}
}
diff --git a/dev/tests/verification/Resources/StaticChecks/mftf-deprecated-entity-usage-checks.txt b/dev/tests/verification/Resources/StaticChecks/mftf-deprecated-entity-usage-checks.txt
new file mode 100644
index 000000000..63e025470
--- /dev/null
+++ b/dev/tests/verification/Resources/StaticChecks/mftf-deprecated-entity-usage-checks.txt
@@ -0,0 +1,23 @@
+
+File "/verification/DeprecationCheckModule/Test/DeprecationCheckTest.xml" contains:
+ - Deprecated Page(s):
+ "DeprecationCheckPage" in /verification/DeprecationCheckModule/Page/DeprecationCheckPage.xml
+ - Deprecated ActionGroup(s):
+ "DeprecationCheckDeprecatedActionGroup" in /verification/DeprecationCheckModule/ActionGroup/DeprecationCheckDeprecatedActionGroup.xml
+ - Deprecated Data(s):
+ "DeprecationCheckData" in /verification/DeprecationCheckModule/Data/DeprecationCheckData.xml
+ - "DeprecationCheckData" references deprecated:
+ "type1" in metadata xml file
+
+
+File "/verification/DeprecationCheckModule/ActionGroup/DeprecationCheckActionGroup.xml" contains:
+ - Deprecated Section(s):
+ "DeprecationCheckSection" in /verification/DeprecationCheckModule/Section/DeprecationCheckSection.xml
+ - Deprecated Element(s):
+ "DeprecationCheckSection.deprecationCheckElement" in /verification/DeprecationCheckModule/Section/DeprecationCheckSection.xml
+
+
+File "/verification/DeprecationCheckModule/Suite/deprecationCheckSuite.xml" contains:
+ - Deprecated Test(s):
+ "DeprecationCheckDeprecatedTest" in /verification/DeprecationCheckModule/Test/DeprecationCheckDeprecatedTest.xml
+
diff --git a/dev/tests/verification/Resources/StaticChecks/mftf-pause-action-usage-checks.txt b/dev/tests/verification/Resources/StaticChecks/mftf-pause-action-usage-checks.txt
new file mode 100644
index 000000000..fd042a24c
--- /dev/null
+++ b/dev/tests/verification/Resources/StaticChecks/mftf-pause-action-usage-checks.txt
@@ -0,0 +1,30 @@
+
+File "/verification/PauseCheckModule/ActionGroup/ActionGroupWithMultiplePausesActionGroup.xml"
+contains pause action(s):
+
+ ActionGroupWithMultiplePausesActionGroup has pause action at stepKey(s): pauseAfterFillField1, pauseAfterFillField2, pauseAfterFillField3
+
+File "/verification/PauseCheckModule/ActionGroup/ActionGroupWithPauseActionGroup.xml"
+contains pause action(s):
+
+ ActionGroupWithPauseActionGroup has pause action at stepKey(s): pauseAfterFillField2
+
+File "/verification/PauseCheckModule/Test/TestWithMultiplePauseActionsTest.xml"
+contains pause action(s):
+
+ TestWithMultiplePauseActionsTest has pause action at stepKey(s): pauseBeforeAmOnPageKey, pauseAfterStep2, pauseAfterAmOnPageKey
+
+File "/verification/PauseCheckModule/Test/TestWithPauseActionTest.xml"
+contains pause action(s):
+
+ TestWithPauseActionTest has pause action at stepKey(s): pauseAfterStep3
+
+File "/verification/PauseCheckModule/Suite/suiteWithMultiplePauseActionsSuite.xml"
+contains pause action(s):
+
+ suiteWithMultiplePauseActionsSuite has pause action at stepKey(s): pauseCreate1, pauseFillAfter
+
+File "/verification/PauseCheckModule/Suite/suiteWithPauseActionSuite.xml"
+contains pause action(s):
+
+ suiteWithPauseActionSuite has pause action at stepKey(s): pauseSuite
diff --git a/dev/tests/verification/Resources/functionalSuiteHooks.txt b/dev/tests/verification/Resources/functionalSuiteHooks.txt
index 3b5f6bae0..3156a59dd 100644
--- a/dev/tests/verification/Resources/functionalSuiteHooks.txt
+++ b/dev/tests/verification/Resources/functionalSuiteHooks.txt
@@ -6,6 +6,11 @@ use Facebook\WebDriver\Remote\RemoteWebDriver;
use Magento\FunctionalTestingFramework\DataGenerator\Handlers\PersistedObjectHandler;
use Magento\FunctionalTestingFramework\DataGenerator\Handlers\CredentialStore;
use Magento\FunctionalTestingFramework\Module\MagentoWebDriver;
+use Magento\FunctionalTestingFramework\Module\MagentoAssert;
+use Magento\FunctionalTestingFramework\Module\MagentoActionProxies;
+use Magento\FunctionalTestingFramework\Exceptions\TestFrameworkException;
+use Codeception\Lib\ModuleContainer;
+use Codeception\Module;
/**
* Group class is Codeception Extension which is allowed to handle to all internal events.
@@ -24,9 +29,15 @@ class functionalSuiteHooks extends \Codeception\GroupObject
private $currentTestRun = 0;
private static $HOOK_EXECUTION_INIT = "\n/******** Beginning execution of functionalSuiteHooks suite %s block ********/\n";
private static $HOOK_EXECUTION_END = "\n/******** Execution of functionalSuiteHooks suite %s block complete ********/\n";
+ /** @var MagentoWebDriver */
+ private $webDriver;
+ /** @var ModuleContainer */
+ private $moduleContainer;
public function _before(\Codeception\Event\TestEvent $e)
{
+ $this->webDriver = $this->getModule('\Magento\FunctionalTestingFramework\Module\MagentoWebDriver');
+ $this->moduleContainer = $this->webDriver->getModuleContainer();
// increment test count per execution
$this->currentTestRun++;
$this->executePreConditions();
@@ -37,41 +48,56 @@ class functionalSuiteHooks extends \Codeception\GroupObject
}
}
-
private function executePreConditions()
{
if ($this->currentTestRun == 1) {
print sprintf(self::$HOOK_EXECUTION_INIT, "before");
- /** @var MagentoWebDriver $webDriver */
- $webDriver = $this->getModule('\Magento\FunctionalTestingFramework\Module\MagentoWebDriver');
-
try {
- if ($webDriver->webDriver != null) {
- $webDriver->_restart();
+ if ($this->webDriver->webDriver != null) {
+ $this->webDriver->_restart();
} else {
- $webDriver->_initializeSession();
+ $this->webDriver->_initializeSession();
}
- $webDriver->amOnPage("some.url"); // stepKey: before
- $createFields['someKey'] = "dataHere";
+ $this->getModuleForAction("amOnPage")->amOnPage("some.url"); // stepKey: before
+ $createOneFields['someKey'] = "dataHere";
+ PersistedObjectHandler::getInstance()->createEntity(
+ "createOne",
+ "suite",
+ "createEntityOne",
+ [],
+ $createOneFields
+ );
+ PersistedObjectHandler::getInstance()->createEntity(
+ "createTwo",
+ "suite",
+ "createEntityTwo",
+ ["createEntityOne"]
+ );
+ PersistedObjectHandler::getInstance()->createEntity(
+ "createThree",
+ "suite",
+ "createEntityThree",
+ []
+ );
PersistedObjectHandler::getInstance()->createEntity(
- "create",
+ "createFour",
"suite",
- "createThis",
- $createFields
+ "createEntityFour",
+ ["createEntityTwo", "createEntityThree"]
);
- $webDriver->click(PersistedObjectHandler::getInstance()->retrieveEntityField('create', 'data', 'suite')); // stepKey: clickWithData
+ $this->getModuleForAction("click")->click(PersistedObjectHandler::getInstance()->retrieveEntityField('createTwo', 'data', 'suite')); // stepKey: clickWithData
print("Entering Action Group [AC] actionGroupWithTwoArguments");
- $webDriver->see("John", msq("uniqueData") . "John"); // stepKey: seeFirstNameAC
+ $this->getModuleForAction("see")->see("John", msq("uniqueData") . "John"); // stepKey: seeFirstNameAC
print("Exiting Action Group [AC] actionGroupWithTwoArguments");
} catch (\Exception $exception) {
$this->preconditionFailure = $exception->getMessage();
}
// reset configuration and close session
- $webDriver->_resetConfig();
- $webDriver->webDriver->close();
- $webDriver->webDriver = null;
+ $this->webDriver->_resetConfig();
+ $this->webDriver->webDriver->close();
+ $this->webDriver->webDriver = null;
print sprintf(self::$HOOK_EXECUTION_END, "before");
}
@@ -82,15 +108,11 @@ class functionalSuiteHooks extends \Codeception\GroupObject
$this->executePostConditions($e);
}
-
private function executePostConditions(\Codeception\Event\TestEvent $e)
{
if ($this->currentTestRun == $this->testCount) {
print sprintf(self::$HOOK_EXECUTION_INIT, "after");
- /** @var MagentoWebDriver $webDriver */
- $webDriver = $this->getModule('\Magento\FunctionalTestingFramework\Module\MagentoWebDriver');
-
try {
// Find out if Test in Suite failed, will cause potential failures in suite after
$cest = $e->getTest();
@@ -113,15 +135,15 @@ class functionalSuiteHooks extends \Codeception\GroupObject
}
}
}
- if ($webDriver->webDriver != null) {
- $webDriver->_restart();
+ if ($this->webDriver->webDriver != null) {
+ $this->webDriver->_restart();
} else {
- $webDriver->_initializeSession();
+ $this->webDriver->_initializeSession();
}
- $webDriver->amOnPage("some.url"); // stepKey: after
- $webDriver->deleteEntityByUrl("deleteThis"); // stepKey: delete
+ $this->getModuleForAction("amOnPage")->amOnPage("some.url"); // stepKey: after
+ $this->getModuleForAction("deleteEntityByUrl")->deleteEntityByUrl("deleteThis"); // stepKey: delete
print("Entering Action Group [AC] actionGroupWithTwoArguments");
- $webDriver->see("John", msq("uniqueData") . "John"); // stepKey: seeFirstNameAC
+ $this->getModuleForAction("see")->see("John", msq("uniqueData") . "John"); // stepKey: seeFirstNameAC
print("Exiting Action Group [AC] actionGroupWithTwoArguments");
} catch (\Exception $exception) {
print $exception->getMessage();
@@ -129,7 +151,7 @@ class functionalSuiteHooks extends \Codeception\GroupObject
PersistedObjectHandler::getInstance()->clearSuiteObjects();
- $this->closeSession($webDriver);
+ $this->closeSession($this->webDriver);
print sprintf(self::$HOOK_EXECUTION_END, "after");
}
@@ -139,13 +161,12 @@ class functionalSuiteHooks extends \Codeception\GroupObject
* Close session method closes current session.
* If config 'close_all_sessions' is set to 'true' all sessions will be closed.
*
- * @param MagentoWebDriver $webDriver
* return void
*/
- private function closeSession(MagentoWebDriver $webDriver): void
+ private function closeSession(): void
{
- $webDriverConfig = $webDriver->_getConfig();
- $webDriver->_closeSession();
+ $webDriverConfig = $this->webDriver->_getConfig();
+ $this->webDriver->_closeSession();
if (isset($webDriverConfig['close_all_sessions']) && $webDriverConfig['close_all_sessions'] === "true") {
$wdHost = sprintf(
'%s://%s:%s%s',
@@ -161,4 +182,20 @@ class functionalSuiteHooks extends \Codeception\GroupObject
}
}
}
+
+ /**
+ * Return the module for an action.
+ *
+ * @param string $action
+ * @return Module
+ * @throws \Exception
+ */
+ private function getModuleForAction($action)
+ {
+ $module = $this->moduleContainer->moduleForAction($action);
+ if ($module === null) {
+ throw new TestFrameworkException('Invalid action "' . $action . '"' . PHP_EOL);
+ }
+ return $module;
+ }
}
diff --git a/dev/tests/verification/Resources/functionalSuiteWithComments.txt b/dev/tests/verification/Resources/functionalSuiteWithComments.txt
index c8523d0c1..02b50f092 100644
--- a/dev/tests/verification/Resources/functionalSuiteWithComments.txt
+++ b/dev/tests/verification/Resources/functionalSuiteWithComments.txt
@@ -6,6 +6,11 @@ use Facebook\WebDriver\Remote\RemoteWebDriver;
use Magento\FunctionalTestingFramework\DataGenerator\Handlers\PersistedObjectHandler;
use Magento\FunctionalTestingFramework\DataGenerator\Handlers\CredentialStore;
use Magento\FunctionalTestingFramework\Module\MagentoWebDriver;
+use Magento\FunctionalTestingFramework\Module\MagentoAssert;
+use Magento\FunctionalTestingFramework\Module\MagentoActionProxies;
+use Magento\FunctionalTestingFramework\Exceptions\TestFrameworkException;
+use Codeception\Lib\ModuleContainer;
+use Codeception\Module;
/**
* Group class is Codeception Extension which is allowed to handle to all internal events.
@@ -24,9 +29,15 @@ class functionalSuiteWithComments extends \Codeception\GroupObject
private $currentTestRun = 0;
private static $HOOK_EXECUTION_INIT = "\n/******** Beginning execution of functionalSuiteWithComments suite %s block ********/\n";
private static $HOOK_EXECUTION_END = "\n/******** Execution of functionalSuiteWithComments suite %s block complete ********/\n";
+ /** @var MagentoWebDriver */
+ private $webDriver;
+ /** @var ModuleContainer */
+ private $moduleContainer;
public function _before(\Codeception\Event\TestEvent $e)
{
+ $this->webDriver = $this->getModule('\Magento\FunctionalTestingFramework\Module\MagentoWebDriver');
+ $this->moduleContainer = $this->webDriver->getModuleContainer();
// increment test count per execution
$this->currentTestRun++;
$this->executePreConditions();
@@ -37,43 +48,40 @@ class functionalSuiteWithComments extends \Codeception\GroupObject
}
}
-
private function executePreConditions()
{
if ($this->currentTestRun == 1) {
print sprintf(self::$HOOK_EXECUTION_INIT, "before");
- /** @var MagentoWebDriver $webDriver */
- $webDriver = $this->getModule('\Magento\FunctionalTestingFramework\Module\MagentoWebDriver');
-
try {
- if ($webDriver->webDriver != null) {
- $webDriver->_restart();
+ if ($this->webDriver->webDriver != null) {
+ $this->webDriver->_restart();
} else {
- $webDriver->_initializeSession();
+ $this->webDriver->_initializeSession();
}
print("Comment in Before");
- $webDriver->amOnPage("some.url"); // stepKey: before
+ $this->getModuleForAction("amOnPage")->amOnPage("some.url"); // stepKey: before
$createFields['someKey'] = "dataHere";
PersistedObjectHandler::getInstance()->createEntity(
"create",
"suite",
"createThis",
- $createFields
+ [],
+ $createFields
);
print("");
- $webDriver->click(PersistedObjectHandler::getInstance()->retrieveEntityField('create', 'data', 'suite')); // stepKey: clickWithData
+ $this->getModuleForAction("click")->click(PersistedObjectHandler::getInstance()->retrieveEntityField('create', 'data', 'suite')); // stepKey: clickWithData
print("Entering Action Group [AC] actionGroupWithTwoArguments");
- $webDriver->see("John", msq("uniqueData") . "John"); // stepKey: seeFirstNameAC
+ $this->getModuleForAction("see")->see("John", msq("uniqueData") . "John"); // stepKey: seeFirstNameAC
print("Exiting Action Group [AC] actionGroupWithTwoArguments");
} catch (\Exception $exception) {
$this->preconditionFailure = $exception->getMessage();
}
// reset configuration and close session
- $webDriver->_resetConfig();
- $webDriver->webDriver->close();
- $webDriver->webDriver = null;
+ $this->webDriver->_resetConfig();
+ $this->webDriver->webDriver->close();
+ $this->webDriver->webDriver = null;
print sprintf(self::$HOOK_EXECUTION_END, "before");
}
@@ -84,15 +92,11 @@ class functionalSuiteWithComments extends \Codeception\GroupObject
$this->executePostConditions($e);
}
-
private function executePostConditions(\Codeception\Event\TestEvent $e)
{
if ($this->currentTestRun == $this->testCount) {
print sprintf(self::$HOOK_EXECUTION_INIT, "after");
- /** @var MagentoWebDriver $webDriver */
- $webDriver = $this->getModule('\Magento\FunctionalTestingFramework\Module\MagentoWebDriver');
-
try {
// Find out if Test in Suite failed, will cause potential failures in suite after
$cest = $e->getTest();
@@ -115,10 +119,10 @@ class functionalSuiteWithComments extends \Codeception\GroupObject
}
}
}
- if ($webDriver->webDriver != null) {
- $webDriver->_restart();
+ if ($this->webDriver->webDriver != null) {
+ $this->webDriver->_restart();
} else {
- $webDriver->_initializeSession();
+ $this->webDriver->_initializeSession();
}
print("afterBlock");
} catch (\Exception $exception) {
@@ -127,7 +131,7 @@ class functionalSuiteWithComments extends \Codeception\GroupObject
PersistedObjectHandler::getInstance()->clearSuiteObjects();
- $this->closeSession($webDriver);
+ $this->closeSession($this->webDriver);
print sprintf(self::$HOOK_EXECUTION_END, "after");
}
@@ -137,13 +141,12 @@ class functionalSuiteWithComments extends \Codeception\GroupObject
* Close session method closes current session.
* If config 'close_all_sessions' is set to 'true' all sessions will be closed.
*
- * @param MagentoWebDriver $webDriver
* return void
*/
- private function closeSession(MagentoWebDriver $webDriver): void
+ private function closeSession(): void
{
- $webDriverConfig = $webDriver->_getConfig();
- $webDriver->_closeSession();
+ $webDriverConfig = $this->webDriver->_getConfig();
+ $this->webDriver->_closeSession();
if (isset($webDriverConfig['close_all_sessions']) && $webDriverConfig['close_all_sessions'] === "true") {
$wdHost = sprintf(
'%s://%s:%s%s',
@@ -159,4 +162,20 @@ class functionalSuiteWithComments extends \Codeception\GroupObject
}
}
}
+
+ /**
+ * Return the module for an action.
+ *
+ * @param string $action
+ * @return Module
+ * @throws \Exception
+ */
+ private function getModuleForAction($action)
+ {
+ $module = $this->moduleContainer->moduleForAction($action);
+ if ($module === null) {
+ throw new TestFrameworkException('Invalid action "' . $action . '"' . PHP_EOL);
+ }
+ return $module;
+ }
}
diff --git a/dev/tests/verification/TestModule/ActionGroup/BasicActionGroup/ActionGroupReturningValueActionGroup.xml b/dev/tests/verification/TestModule/ActionGroup/BasicActionGroup/ActionGroupReturningValueActionGroup.xml
new file mode 100644
index 000000000..ddb93c0c1
--- /dev/null
+++ b/dev/tests/verification/TestModule/ActionGroup/BasicActionGroup/ActionGroupReturningValueActionGroup.xml
@@ -0,0 +1,21 @@
+
+
+
+
+
+
+
+
+
+ {{count}}
+ grabProducts1
+
+
+
+
diff --git a/dev/tests/verification/TestModule/ActionGroup/BasicActionGroup/ExtendedActionGroupReturningValueActionGroup.xml b/dev/tests/verification/TestModule/ActionGroup/BasicActionGroup/ExtendedActionGroupReturningValueActionGroup.xml
new file mode 100644
index 000000000..18933a2f2
--- /dev/null
+++ b/dev/tests/verification/TestModule/ActionGroup/BasicActionGroup/ExtendedActionGroupReturningValueActionGroup.xml
@@ -0,0 +1,21 @@
+
+
+
+
+
+
+
+
+
+ {{otherCount}}
+ grabProducts2
+
+
+
+
diff --git a/dev/tests/verification/TestModule/ActionGroup/FunctionalActionGroup/FunctionalActionGroupWithReturnValueActionGroup.xml b/dev/tests/verification/TestModule/ActionGroup/FunctionalActionGroup/FunctionalActionGroupWithReturnValueActionGroup.xml
new file mode 100644
index 000000000..77900efd3
--- /dev/null
+++ b/dev/tests/verification/TestModule/ActionGroup/FunctionalActionGroup/FunctionalActionGroupWithReturnValueActionGroup.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
diff --git a/dev/tests/verification/TestModule/ActionGroup/FunctionalActionGroup/MergeActionGroupReturningValueActionGroup.xml b/dev/tests/verification/TestModule/ActionGroup/FunctionalActionGroup/MergeActionGroupReturningValueActionGroup.xml
new file mode 100644
index 000000000..83afbce0c
--- /dev/null
+++ b/dev/tests/verification/TestModule/ActionGroup/FunctionalActionGroup/MergeActionGroupReturningValueActionGroup.xml
@@ -0,0 +1,19 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/dev/tests/verification/TestModule/ActionGroup/MergeFunctionalActionGroup/MergeActionGroupReturningValueActionGroup.xml b/dev/tests/verification/TestModule/ActionGroup/MergeFunctionalActionGroup/MergeActionGroupReturningValueActionGroup.xml
new file mode 100644
index 000000000..d952fdddf
--- /dev/null
+++ b/dev/tests/verification/TestModule/ActionGroup/MergeFunctionalActionGroup/MergeActionGroupReturningValueActionGroup.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
diff --git a/dev/tests/verification/TestModule/Data/PersistedReplacementData.xml b/dev/tests/verification/TestModule/Data/PersistedReplacementData.xml
index 514b5eec6..7213fa6b9 100644
--- a/dev/tests/verification/TestModule/Data/PersistedReplacementData.xml
+++ b/dev/tests/verification/TestModule/Data/PersistedReplacementData.xml
@@ -22,4 +22,8 @@
Dane
unmerged
+
+ some/data
+ {{_CREDS.magento/some/secret}}
+
diff --git a/dev/tests/verification/TestModule/Suite/ActionsInDifferentModulesSuite.xml b/dev/tests/verification/TestModule/Suite/ActionsInDifferentModulesSuite.xml
new file mode 100644
index 000000000..e3553308d
--- /dev/null
+++ b/dev/tests/verification/TestModule/Suite/ActionsInDifferentModulesSuite.xml
@@ -0,0 +1,43 @@
+
+
+
+
+
+
+
+
+
+
+
+ dataHere
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/dev/tests/verification/TestModule/Suite/functionalSuiteHooks.xml b/dev/tests/verification/TestModule/Suite/functionalSuiteHooks.xml
index 4fc459e7d..b4dca7f8d 100644
--- a/dev/tests/verification/TestModule/Suite/functionalSuiteHooks.xml
+++ b/dev/tests/verification/TestModule/Suite/functionalSuiteHooks.xml
@@ -13,10 +13,18 @@
-
+
dataHere
-
+
+
+
+
+
+
+
+
+
diff --git a/dev/tests/verification/TestModule/Test/ActionGroupFunctionalTest/ActionGroupReturningValueTest.xml b/dev/tests/verification/TestModule/Test/ActionGroupFunctionalTest/ActionGroupReturningValueTest.xml
new file mode 100644
index 000000000..ade1f51db
--- /dev/null
+++ b/dev/tests/verification/TestModule/Test/ActionGroupFunctionalTest/ActionGroupReturningValueTest.xml
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/dev/tests/verification/TestModule/Test/ActionGroupTest/ExtendedActionGroupReturningValueTest.xml b/dev/tests/verification/TestModule/Test/ActionGroupTest/ExtendedActionGroupReturningValueTest.xml
new file mode 100644
index 000000000..68042344c
--- /dev/null
+++ b/dev/tests/verification/TestModule/Test/ActionGroupTest/ExtendedActionGroupReturningValueTest.xml
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/dev/tests/verification/TestModule/Test/ActionGroupTest/ExtendedChildActionGroupReturningValueTest.xml b/dev/tests/verification/TestModule/Test/ActionGroupTest/ExtendedChildActionGroupReturningValueTest.xml
new file mode 100644
index 000000000..ecd46c3c4
--- /dev/null
+++ b/dev/tests/verification/TestModule/Test/ActionGroupTest/ExtendedChildActionGroupReturningValueTest.xml
@@ -0,0 +1,23 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/dev/tests/verification/TestModule/Test/MergeFunctionalTest/MergedActionGroupReturningValueTest.xml b/dev/tests/verification/TestModule/Test/MergeFunctionalTest/MergedActionGroupReturningValueTest.xml
new file mode 100644
index 000000000..baf7c0ded
--- /dev/null
+++ b/dev/tests/verification/TestModule/Test/MergeFunctionalTest/MergedActionGroupReturningValueTest.xml
@@ -0,0 +1,32 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/dev/tests/verification/TestModule/Test/SampleSuiteTest/IncludeActionsInDifferentModulesTest.xml b/dev/tests/verification/TestModule/Test/SampleSuiteTest/IncludeActionsInDifferentModulesTest.xml
new file mode 100644
index 000000000..1be662bbe
--- /dev/null
+++ b/dev/tests/verification/TestModule/Test/SampleSuiteTest/IncludeActionsInDifferentModulesTest.xml
@@ -0,0 +1,20 @@
+
+
+
+
+
+ dataHere
+
+
+
+
+
+
+
+
diff --git a/dev/tests/verification/Tests/ActionGroupWithReturnGenerationTest.php b/dev/tests/verification/Tests/ActionGroupWithReturnGenerationTest.php
new file mode 100644
index 000000000..e369a797d
--- /dev/null
+++ b/dev/tests/verification/Tests/ActionGroupWithReturnGenerationTest.php
@@ -0,0 +1,52 @@
+generateAndCompareTest('ActionGroupReturningValueTest');
+ }
+ /**
+ * Test generation of a test referencing a merged action group that returns a value.
+ *
+ * @throws \Exception
+ * @throws \Magento\FunctionalTestingFramework\Exceptions\TestReferenceException
+ */
+ public function testMergedActionGroupReturningValue()
+ {
+ $this->generateAndCompareTest('MergedActionGroupReturningValueTest');
+ }
+ /**
+ * Test generation of a test referencing an extended action group that returns a value.
+ *
+ * @throws \Exception
+ * @throws \Magento\FunctionalTestingFramework\Exceptions\TestReferenceException
+ */
+ public function testExtendedActionGroupReturningValue()
+ {
+ $this->generateAndCompareTest('ExtendedActionGroupReturningValueTest');
+ }
+ /**
+ * Test generation of a test referencing an extending child action group that returns a value.
+ *
+ * @throws \Exception
+ * @throws \Magento\FunctionalTestingFramework\Exceptions\TestReferenceException
+ */
+ public function testExtendedChildActionGroupReturningValue()
+ {
+ $this->generateAndCompareTest('ExtendedChildActionGroupReturningValueTest');
+ }
+}
diff --git a/dev/tests/verification/Tests/StaticCheck/DeprecationStaticCheckTest.php b/dev/tests/verification/Tests/StaticCheck/DeprecationStaticCheckTest.php
new file mode 100644
index 000000000..f6aef65f6
--- /dev/null
+++ b/dev/tests/verification/Tests/StaticCheck/DeprecationStaticCheckTest.php
@@ -0,0 +1,50 @@
+mockInputInterface(self::TEST_MODULE_PATH);
+ AspectMock::double(StaticChecksList::class, ['getErrorFilesPath' => self::STATIC_RESULTS_DIR]);
+
+ /** @var InputInterface $input */
+ $staticCheck->execute($input);
+
+ $this->assertTrue(file_exists(self::LOG_FILE));
+ $this->assertFileEquals(
+ self::RESOURCES_PATH.
+ DIRECTORY_SEPARATOR .
+ DeprecatedEntityUsageCheck::ERROR_LOG_FILENAME .
+ ".txt",
+ self::LOG_FILE
+ );
+ }
+}
diff --git a/dev/tests/verification/Tests/StaticCheck/PauseActionStaticCheckTest.php b/dev/tests/verification/Tests/StaticCheck/PauseActionStaticCheckTest.php
new file mode 100644
index 000000000..7c8f77c2f
--- /dev/null
+++ b/dev/tests/verification/Tests/StaticCheck/PauseActionStaticCheckTest.php
@@ -0,0 +1,51 @@
+mockInputInterface(self::TEST_MODULE_PATH);
+ AspectMock::double(StaticChecksList::class, ['getErrorFilesPath' => self::STATIC_RESULTS_DIR]);
+
+ /** @var InputInterface $input */
+ $staticCheck->execute($input);
+
+ $this->assertTrue(file_exists(self::LOG_FILE));
+ $this->assertFileEquals(
+ self::RESOURCES_PATH.
+ DIRECTORY_SEPARATOR .
+ PauseActionUsageCheck::ERROR_LOG_FILENAME .
+ ".txt",
+ self::LOG_FILE
+ );
+ }
+}
diff --git a/dev/tests/verification/Tests/SuiteGenerationTest.php b/dev/tests/verification/Tests/SuiteGenerationTest.php
index 4a8371692..f37283871 100644
--- a/dev/tests/verification/Tests/SuiteGenerationTest.php
+++ b/dev/tests/verification/Tests/SuiteGenerationTest.php
@@ -388,6 +388,65 @@ public function testSuiteCommentsGeneration()
);
}
+ /**
+ * Test suite generation with actions from different modules
+ */
+ public function testSuiteGenerationActionsInDifferentModules()
+ {
+ $groupName = 'ActionsInDifferentModulesSuite';
+
+ $expectedContents = [
+ 'IncludeActionsInDifferentModulesTestCest.php'
+ ];
+
+ // Generate the Suite
+ SuiteGenerator::getInstance()->generateSuite($groupName);
+
+ // Validate log message and add group name for later deletion
+ TestLoggingUtil::getInstance()->validateMockLogStatement(
+ 'info',
+ "suite generated",
+ ['suite' => $groupName, 'relative_path' => "_generated" . DIRECTORY_SEPARATOR . $groupName]
+ );
+ self::$TEST_GROUPS[] = $groupName;
+
+ // Validate Yaml file updated
+ $yml = Yaml::parse(file_get_contents(self::CONFIG_YML_FILE));
+ $this->assertArrayHasKey($groupName, $yml['groups']);
+
+ $suiteResultBaseDir = self::GENERATE_RESULT_DIR .
+ DIRECTORY_SEPARATOR .
+ $groupName .
+ DIRECTORY_SEPARATOR;
+
+ // Validate tests have been generated
+ $dirContents = array_diff(scandir($suiteResultBaseDir), ['..', '.']);
+
+ foreach ($expectedContents as $expectedFile) {
+ $this->assertTrue(in_array($expectedFile, $dirContents));
+ }
+
+ //assert group file created and contains correct contents
+ $groupFile = PROJECT_ROOT .
+ DIRECTORY_SEPARATOR .
+ "src" .
+ DIRECTORY_SEPARATOR .
+ "Magento" .
+ DIRECTORY_SEPARATOR .
+ "FunctionalTestingFramework" .
+ DIRECTORY_SEPARATOR .
+ "Group" .
+ DIRECTORY_SEPARATOR .
+ $groupName .
+ ".php";
+
+ $this->assertTrue(file_exists($groupFile));
+ $this->assertFileEquals(
+ self::RESOURCES_PATH . DIRECTORY_SEPARATOR . $groupName . ".txt",
+ $groupFile
+ );
+ }
+
/**
* revert any changes made to config.yml
* remove _generated directory
diff --git a/docs/commands/mftf.md b/docs/commands/mftf.md
index b7981808c..aabc8dadb 100644
--- a/docs/commands/mftf.md
+++ b/docs/commands/mftf.md
@@ -449,7 +449,7 @@ vendor/bin/mftf static-checks []...
| Option | Description |
|-----------------------|-----------------------------------------------------------------------------------------------------------|
-| `-p, --path` | Path to a MFTF test module to run "deprecatedEntityUsage" static check script. Option is ignored by other static check scripts.
+| `-p, --path` | Path to a MFTF test module to run "deprecatedEntityUsage" and "pauseActionUsage" static check scripts. Option is ignored by other static check scripts.
#### Examples
@@ -479,6 +479,10 @@ vendor/bin/mftf static-checks actionGroupArguments
vendor/bin/mftf static-checks deprecatedEntityUsage
```
+```bash
+vendor/bin/mftf static-checks pauseActionUsage
+```
+
```bash
vendor/bin/mftf static-checks annotations
```
@@ -487,6 +491,10 @@ vendor/bin/mftf static-checks annotations
vendor/bin/mftf static-checks deprecatedEntityUsage -p path/to/mftf/test/module
```
+```bash
+vendor/bin/mftf static-checks pauseActionUsage -p path/to/mftf/test/module
+```
+
```bash
vendor/bin/mftf static-checks testDependencies actionGroupArguments
```
@@ -499,6 +507,7 @@ vendor/bin/mftf static-checks testDependencies actionGroupArguments
|`actionGroupArguments` | Checks that action groups do not have unused arguments.|
|`deprecatedEntityUsage`| Checks that deprecated test entities are not being referenced.|
|`annotations`| Checks various details of test annotations, such as missing annotations or duplicate annotations.|
+|`pauseUsage`| Checks that pause action is not used in action groups, tests or suites.|
#### Defining ruleset
@@ -559,6 +568,47 @@ To upgrade all test components inside the `Catalog` module:
vendor/bin/mftf upgrade:tests /Users/user/magento2/app/code/Magento/Catalog/Test/Mftf/
```
+### `codecept:run`
+
+A MFTF wrapper command that invokes `vendor/bin/codecept run`. This command runs tests in functional suite. Tests must be generated before using this command.
+
+#### Usage
+
+See the [Run Command](https://codeception.com/docs/reference/Commands#Run).
+
+```bash
+vendor/bin/mftf codecept:run [] --[
```
+## Return a value
+
+Action groups can return a value using a `return` tag.
+
+```xml
+
+
+
+
+
+```
+
+The value returned can be accessed in later steps using action group step key `{$getOrderId}`.
+```xml
+
+
+
+
+
+```
+### Convention to return a value
+
+The following conventions apply to action groups returning a value:
+- Only action groups can return value. Use of `return` tag is dis-allowed in tests and suites.
+- An action group does not support multiple `return` tags.
+- For [merging action groups](../merging.md#merge-action-groups), `return` is allowed only in one of the merging action groups.
+- Value returned by an action group can only be referenced within the scope that the action group is defined in (`test`, `before/after`).
+
## Optimizing action group structures
Structuring properly an action group increases code reusability and readability.
diff --git a/docs/test/actions.md b/docs/test/actions.md
index 9a51106de..42ecde053 100644
--- a/docs/test/actions.md
+++ b/docs/test/actions.md
@@ -151,6 +151,7 @@ The following test actions return a variable:
* [grabValueFrom](#grabvaluefrom)
* [executeJS](#executejs)
* [getOTP](#getotp)
+* [return](#return)
Learn more in [Using data returned by test actions](../data.md#use-data-returned-by-test-actions).
@@ -1240,6 +1241,22 @@ To access this value, use `{$grabInputName}` in later actions. -->
```
+### return
+
+Specifies what value is returned by an action group. The value can be then accessed in later steps using the action group stepKey. See [Action groups returning a value](./action-groups.md#return-a-value) for usage information.
+
+Attribute|Type|Use|Description
+---|---|---|---
+`value`|string|required| value returned by action group.
+`stepKey`|string|required| A unique identifier of the action.
+
+#### Example
+
+```xml
+
+
+```
+
### loadSessionSnapshot
See [loadSessionSnapshot docs on codeception.com](http://codeception.com/docs/modules/WebDriver#loadSessionSnapshot).
@@ -1447,7 +1464,7 @@ Attribute|Type|Use|Description
### pause
-See [pause docs on codeception.com](https://codeception.com/docs/02-GettingStarted).
+See usage of `
+
]>
diff --git a/src/Magento/FunctionalTestingFramework/Console/BaseGenerateCommand.php b/src/Magento/FunctionalTestingFramework/Console/BaseGenerateCommand.php
index f3becf935..401153c7b 100644
--- a/src/Magento/FunctionalTestingFramework/Console/BaseGenerateCommand.php
+++ b/src/Magento/FunctionalTestingFramework/Console/BaseGenerateCommand.php
@@ -14,6 +14,7 @@
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
+use Symfony\Component\Console\Input\StringInput;
use Symfony\Component\Console\Output\OutputInterface;
use Magento\FunctionalTestingFramework\Util\Filesystem\DirSetupUtil;
use Magento\FunctionalTestingFramework\Util\TestGenerator;
@@ -21,9 +22,25 @@
use Magento\FunctionalTestingFramework\Suite\Handlers\SuiteObjectHandler;
use Symfony\Component\Console\Style\SymfonyStyle;
+/**
+ * Class BaseGenerateCommand
+ * @package Magento\FunctionalTestingFramework\Console
+ *
+ * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
+ */
class BaseGenerateCommand extends Command
{
const MFTF_NOTICES = "Placeholder text for MFTF notices\n";
+ const CODECEPT_RUN = 'codecept:run';
+ const CODECEPT_RUN_FUNCTIONAL = self::CODECEPT_RUN . ' functional ';
+ const CODECEPT_RUN_OPTION_NO_EXIT = ' --no-exit ';
+
+ /**
+ * Enable pause()
+ *
+ * @var boolean
+ */
+ private $enablePause = null;
/**
* Console output style
@@ -218,4 +235,36 @@ protected function showMftfNotices(OutputInterface $output)
$output->writeln(self::MFTF_NOTICES);
}
}
+
+ /**
+ * Return if pause() is enabled
+ *
+ * @return boolean
+ */
+ protected function pauseEnabled()
+ {
+ if (null === $this->enablePause) {
+ if (getenv('ENABLE_PAUSE') === 'true') {
+ $this->enablePause = true;
+ } else {
+ $this->enablePause = false;
+ }
+ }
+ return $this->enablePause;
+ }
+
+ /**
+ * Runs the bin/mftf codecept:run command and returns exit code
+ *
+ * @param string $commandStr
+ * @param OutputInterface $output
+ * @return integer
+ * @throws \Exception
+ */
+ protected function codeceptRunTest(string $commandStr, OutputInterface $output)
+ {
+ $input = new StringInput($commandStr);
+ $command = $this->getApplication()->find(self::CODECEPT_RUN);
+ return $command->run($input, $output);
+ }
}
diff --git a/src/Magento/FunctionalTestingFramework/Console/Codecept/CodeceptCommandUtil.php b/src/Magento/FunctionalTestingFramework/Console/Codecept/CodeceptCommandUtil.php
new file mode 100644
index 000000000..3992aa677
--- /dev/null
+++ b/src/Magento/FunctionalTestingFramework/Console/Codecept/CodeceptCommandUtil.php
@@ -0,0 +1,66 @@
+__toString());
+ $tokens[0] = str_replace('codecept:', '', $tokens[0]);
+ \Closure::bind(function &(ArgvInput $input) use ($tokens) {
+ return $input->setTokens($tokens);
+ }, null, ArgvInput::class);
+ }
+
+ /**
+ * Save Codeception working directory
+ *
+ * @return void
+ * @throws TestFrameworkException
+ */
+ public function setCodeceptCwd()
+ {
+ $this->cwd = getcwd();
+ chdir(FilePathFormatter::format(TESTS_BP, false));
+ }
+
+ /**
+ * Restore current working directory
+ *
+ * @return void
+ */
+ public function restoreCwd()
+ {
+ if ($this->cwd) {
+ chdir($this->cwd);
+ }
+ }
+}
diff --git a/src/Magento/FunctionalTestingFramework/Console/CodeceptRunCommand.php b/src/Magento/FunctionalTestingFramework/Console/CodeceptRunCommand.php
new file mode 100644
index 000000000..de16bad84
--- /dev/null
+++ b/src/Magento/FunctionalTestingFramework/Console/CodeceptRunCommand.php
@@ -0,0 +1,61 @@
+setName('codecept:run')
+ ->setDescription(
+ "Wrapper command to vendor/bin/codecept:run. See https://codeception.com/docs/reference/Commands#Run"
+ );
+
+ parent::configure();
+ }
+
+ /**
+ * Executes the current command
+ *
+ * @param InputInterface $input
+ * @param OutputInterface $output
+ * @return integer
+ * @throws \Exception
+ */
+ public function execute(InputInterface $input, OutputInterface $output): int
+ {
+ $commandUtil = new CodeceptCommandUtil();
+ $commandUtil->setup($input);
+ $commandUtil->setCodeceptCwd();
+
+ try {
+ $exitCode = parent::execute($input, $output);
+ } catch (\Exception $e) {
+ throw new TestFrameworkException(
+ 'Make sure cest files are generated before running bin/mftf '
+ . $this->getName()
+ . PHP_EOL
+ . $e->getMessage()
+ );
+ }
+
+ $commandUtil->restoreCwd();
+ return $exitCode;
+ }
+}
diff --git a/src/Magento/FunctionalTestingFramework/Console/CommandList.php b/src/Magento/FunctionalTestingFramework/Console/CommandList.php
index f77c2c576..840e7dd86 100644
--- a/src/Magento/FunctionalTestingFramework/Console/CommandList.php
+++ b/src/Magento/FunctionalTestingFramework/Console/CommandList.php
@@ -30,6 +30,7 @@ public function __construct(array $commands = [])
{
$this->commands = [
'build:project' => new BuildProjectCommand(),
+ 'codecept:run' => new CodeceptRunCommand(),
'doctor' => new DoctorCommand(),
'generate:suite' => new GenerateSuiteCommand(),
'generate:tests' => new GenerateTestsCommand(),
diff --git a/src/Magento/FunctionalTestingFramework/Console/RunManifestCommand.php b/src/Magento/FunctionalTestingFramework/Console/RunManifestCommand.php
index 37a90bb77..683be1b53 100644
--- a/src/Magento/FunctionalTestingFramework/Console/RunManifestCommand.php
+++ b/src/Magento/FunctionalTestingFramework/Console/RunManifestCommand.php
@@ -11,6 +11,7 @@
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
+use Symfony\Component\Console\Input\StringInput;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Process\Process;
use Magento\FunctionalTestingFramework\Util\Path\FilePathFormatter;
@@ -80,12 +81,17 @@ protected function execute(InputInterface $input, OutputInterface $output): int
// Delete the Codeception failed file just in case it exists from any previous test runs
$this->deleteFailedFile();
- foreach ($manifestFile as $manifestLine) {
- if (empty($manifestLine)) {
+ for ($line = 0; $line < count($manifestFile); $line++) {
+ if (empty($manifestFile[$line])) {
continue;
}
- $this->runManifestLine($manifestLine, $output);
+ if ($line == count($manifestFile) - 1) {
+ $this->runManifestLine($manifestFile[$line], $output, true);
+ } else {
+ $this->runManifestLine($manifestFile[$line], $output);
+ }
+
$this->aggregateFailed();
}
@@ -102,24 +108,38 @@ protected function execute(InputInterface $input, OutputInterface $output): int
*
* @param string $manifestLine
* @param OutputInterface $output
+ * @param boolean $exit
* @return void
+ * @throws \Exception
*
* @SuppressWarnings(PHPMD.UnusedLocalVariable) Need this because of the unused $type variable in the closure
*/
- private function runManifestLine(string $manifestLine, OutputInterface $output)
+ private function runManifestLine($manifestLine, $output, $exit = false)
{
- $codeceptionCommand = realpath(PROJECT_ROOT . "/vendor/bin/codecept")
- . " run functional --verbose --steps "
- . $manifestLine;
-
- // run the codecept command in a sub process
- $process = new Process($codeceptionCommand);
- $process->setWorkingDirectory(TESTS_BP);
- $process->setIdleTimeout(600);
- $process->setTimeout(0);
- $subReturnCode = $process->run(function ($type, $buffer) use ($output) {
- $output->write($buffer);
- });
+ if (getenv('ENABLE_PAUSE') === 'true') {
+ $codeceptionCommand = BaseGenerateCommand::CODECEPT_RUN_FUNCTIONAL
+ . '--verbose --steps --debug ';
+ if (!$exit) {
+ $codeceptionCommand .= BaseGenerateCommand::CODECEPT_RUN_OPTION_NO_EXIT;
+ }
+ $codeceptionCommand .= $manifestLine;
+ $input = new StringInput($codeceptionCommand);
+ $command = $this->getApplication()->find(BaseGenerateCommand::CODECEPT_RUN);
+ $subReturnCode = $command->run($input, $output);
+ } else {
+ $codeceptionCommand = realpath(PROJECT_ROOT . "/vendor/bin/codecept")
+ . " run functional --verbose --steps " . $manifestLine;
+
+ // run the codecept command in a sub process
+ $process = new Process($codeceptionCommand);
+ $process->setWorkingDirectory(TESTS_BP);
+ $process->setIdleTimeout(600);
+ $process->setTimeout(0);
+ $subReturnCode = $process->run(function ($type, $buffer) use ($output) {
+ $output->write($buffer);
+ });
+ }
+
$this->returnCode = max($this->returnCode, $subReturnCode);
}
diff --git a/src/Magento/FunctionalTestingFramework/Console/RunTestCommand.php b/src/Magento/FunctionalTestingFramework/Console/RunTestCommand.php
index 373256cfc..6931e8303 100644
--- a/src/Magento/FunctionalTestingFramework/Console/RunTestCommand.php
+++ b/src/Magento/FunctionalTestingFramework/Console/RunTestCommand.php
@@ -119,25 +119,40 @@ protected function execute(InputInterface $input, OutputInterface $output): int
* @param OutputInterface $output
* @return void
* @throws TestFrameworkException
+ * @throws \Exception
*/
private function runTests(array $tests, OutputInterface $output)
{
- $codeceptionCommand = realpath(PROJECT_ROOT . '/vendor/bin/codecept') . ' run functional ';
+ if ($this->pauseEnabled()) {
+ $codeceptionCommand = self::CODECEPT_RUN_FUNCTIONAL;
+ } else {
+ $codeceptionCommand = realpath(PROJECT_ROOT . '/vendor/bin/codecept') . ' run functional ';
+ }
+
$testsDirectory = FilePathFormatter::format(TESTS_MODULE_PATH) .
TestGenerator::GENERATED_DIR .
DIRECTORY_SEPARATOR .
TestGenerator::DEFAULT_DIR .
DIRECTORY_SEPARATOR ;
- foreach ($tests as $test) {
- $testName = $test . 'Cest.php';
+ for ($i = 0; $i < count($tests); $i++) {
+ $testName = $tests[$i] . 'Cest.php';
if (!realpath($testsDirectory . $testName)) {
throw new TestFrameworkException(
$testName . " is not available under " . $testsDirectory
);
}
- $fullCommand = $codeceptionCommand . $testsDirectory . $testName . ' --verbose --steps';
- $this->returnCode = max($this->returnCode, $this->executeTestCommand($fullCommand, $output));
+
+ if ($this->pauseEnabled()) {
+ $fullCommand = $codeceptionCommand . $testsDirectory . $testName . ' --verbose --steps --debug';
+ if ($i != count($tests) - 1) {
+ $fullCommand .= self::CODECEPT_RUN_OPTION_NO_EXIT;
+ }
+ $this->returnCode = max($this->returnCode, $this->codeceptRunTest($fullCommand, $output));
+ } else {
+ $fullCommand = $codeceptionCommand . $testsDirectory . $testName . ' --verbose --steps';
+ $this->returnCode = max($this->returnCode, $this->executeTestCommand($fullCommand, $output));
+ }
}
}
@@ -147,14 +162,32 @@ private function runTests(array $tests, OutputInterface $output)
* @param array $suitesConfig
* @param OutputInterface $output
* @return void
+ * @throws \Exception
*/
private function runTestsInSuite(array $suitesConfig, OutputInterface $output)
{
- $codeceptionCommand = realpath(PROJECT_ROOT . '/vendor/bin/codecept') . ' run functional --verbose --steps ';
+ if ($this->pauseEnabled()) {
+ $codeceptionCommand = self::CODECEPT_RUN_FUNCTIONAL . '--verbose --steps --debug';
+ } else {
+ $codeceptionCommand = realpath(PROJECT_ROOT . '/vendor/bin/codecept')
+ . ' run functional --verbose --steps ';
+ }
+
+ $count = count($suitesConfig);
+ $index = 0;
//for tests in suites, run them as a group to run before and after block
foreach (array_keys($suitesConfig) as $suite) {
$fullCommand = $codeceptionCommand . " -g {$suite}";
- $this->returnCode = max($this->returnCode, $this->executeTestCommand($fullCommand, $output));
+
+ $index += 1;
+ if ($this->pauseEnabled()) {
+ if ($index != $count) {
+ $fullCommand .= self::CODECEPT_RUN_OPTION_NO_EXIT;
+ }
+ $this->returnCode = max($this->returnCode, $this->codeceptRunTest($fullCommand, $output));
+ } else {
+ $this->returnCode = max($this->returnCode, $this->executeTestCommand($fullCommand, $output));
+ }
}
}
@@ -173,6 +206,7 @@ private function executeTestCommand(string $command, OutputInterface $output)
$process->setWorkingDirectory(TESTS_BP);
$process->setIdleTimeout(600);
$process->setTimeout(0);
+
return $process->run(function ($type, $buffer) use ($output) {
$output->write($buffer);
});
diff --git a/src/Magento/FunctionalTestingFramework/Console/RunTestFailedCommand.php b/src/Magento/FunctionalTestingFramework/Console/RunTestFailedCommand.php
index c6a2d0b76..6e6954c70 100644
--- a/src/Magento/FunctionalTestingFramework/Console/RunTestFailedCommand.php
+++ b/src/Magento/FunctionalTestingFramework/Console/RunTestFailedCommand.php
@@ -116,19 +116,28 @@ protected function execute(InputInterface $input, OutputInterface $output): int
$testManifestList = $this->readTestManifestFile();
$returnCode = 0;
- foreach ($testManifestList as $testCommand) {
- $codeceptionCommand = realpath(PROJECT_ROOT . '/vendor/bin/codecept') . ' run functional ';
- $codeceptionCommand .= $testCommand;
-
- $process = new Process($codeceptionCommand);
- $process->setWorkingDirectory(TESTS_BP);
- $process->setIdleTimeout(600);
- $process->setTimeout(0);
- $returnCode = max($returnCode, $process->run(
- function ($type, $buffer) use ($output) {
- $output->write($buffer);
+ for ($i = 0; $i < count($testManifestList); $i++) {
+ if ($this->pauseEnabled()) {
+ $codeceptionCommand = self::CODECEPT_RUN_FUNCTIONAL . $testManifestList[$i] . ' --debug ';
+ if ($i != count($testManifestList) - 1) {
+ $codeceptionCommand .= self::CODECEPT_RUN_OPTION_NO_EXIT;
}
- ));
+ $returnCode = $this->codeceptRunTest($codeceptionCommand, $output);
+ } else {
+ $codeceptionCommand = realpath(PROJECT_ROOT . '/vendor/bin/codecept') . ' run functional ';
+ $codeceptionCommand .= $testManifestList[$i];
+
+ $process = new Process($codeceptionCommand);
+ $process->setWorkingDirectory(TESTS_BP);
+ $process->setIdleTimeout(600);
+ $process->setTimeout(0);
+ $returnCode = max($returnCode, $process->run(
+ function ($type, $buffer) use ($output) {
+ $output->write($buffer);
+ }
+ ));
+ }
+
if (file_exists($this->testsFailedFile)) {
$this->failedList = array_merge(
$this->failedList,
@@ -136,6 +145,7 @@ function ($type, $buffer) use ($output) {
);
}
}
+
foreach ($this->failedList as $test) {
$this->writeFailedTestToFile($test, $this->testsFailedFile);
}
diff --git a/src/Magento/FunctionalTestingFramework/Console/RunTestGroupCommand.php b/src/Magento/FunctionalTestingFramework/Console/RunTestGroupCommand.php
index 6ea37785d..f18b9dc30 100644
--- a/src/Magento/FunctionalTestingFramework/Console/RunTestGroupCommand.php
+++ b/src/Magento/FunctionalTestingFramework/Console/RunTestGroupCommand.php
@@ -52,6 +52,7 @@ protected function configure()
* @throws \Exception
*
* @SuppressWarnings(PHPMD.UnusedLocalVariable)
+ * @SuppressWarnings(PHPMD.CyclomaticComplexity)
*/
protected function execute(InputInterface $input, OutputInterface $output): int
{
@@ -94,23 +95,33 @@ protected function execute(InputInterface $input, OutputInterface $output): int
$command->run(new ArrayInput($args), $output);
}
- $commandString = realpath(PROJECT_ROOT . '/vendor/bin/codecept') . ' run functional --verbose --steps';
+ if ($this->pauseEnabled()) {
+ $commandString = self::CODECEPT_RUN_FUNCTIONAL . '--verbose --steps --debug';
+ } else {
+ $commandString = realpath(PROJECT_ROOT . '/vendor/bin/codecept') . ' run functional --verbose --steps';
+ }
$exitCode = -1;
$returnCodes = [];
- foreach ($groups as $group) {
- $codeceptionCommandString = $commandString . " -g {$group}";
-
- $process = new Process($codeceptionCommandString);
- $process->setWorkingDirectory(TESTS_BP);
- $process->setIdleTimeout(600);
- $process->setTimeout(0);
+ for ($i = 0; $i < count($groups); $i++) {
+ $codeceptionCommandString = $commandString . ' -g ' . $groups[$i];
- $returnCodes[] = $process->run(
- function ($type, $buffer) use ($output) {
- $output->write($buffer);
+ if ($this->pauseEnabled()) {
+ if ($i != count($groups) - 1) {
+ $codeceptionCommandString .= self::CODECEPT_RUN_OPTION_NO_EXIT;
}
- );
+ $returnCodes[] = $this->codeceptRunTest($codeceptionCommandString, $output);
+ } else {
+ $process = new Process($codeceptionCommandString);
+ $process->setWorkingDirectory(TESTS_BP);
+ $process->setIdleTimeout(600);
+ $process->setTimeout(0);
+ $returnCodes[] = $process->run(
+ function ($type, $buffer) use ($output) {
+ $output->write($buffer);
+ }
+ );
+ }
}
foreach ($returnCodes as $returnCode) {
diff --git a/src/Magento/FunctionalTestingFramework/Console/StaticChecksCommand.php b/src/Magento/FunctionalTestingFramework/Console/StaticChecksCommand.php
index fe07dbaaf..806508ef8 100644
--- a/src/Magento/FunctionalTestingFramework/Console/StaticChecksCommand.php
+++ b/src/Magento/FunctionalTestingFramework/Console/StaticChecksCommand.php
@@ -151,12 +151,17 @@ private function validateInput(InputInterface $input)
}
if ($input->getOption('path')) {
- if ( (count($this->staticCheckObjects) !== 1)
- || array_keys($this->staticCheckObjects)[0] !== StaticChecksList::DEPRECATED_ENTITY_USAGE_CHECK_NAME )
+ if ((count($this->staticCheckObjects) !== 1)
+ || !in_array(
+ array_keys($this->staticCheckObjects)[0],
+ [
+ StaticChecksList::DEPRECATED_ENTITY_USAGE_CHECK_NAME,
+ StaticChecksList::PAUSE_ACTION_USAGE_CHECK_NAME
+ ]
+ )
+ )
throw new InvalidArgumentException(
- '--path option can only be used for "'
- . StaticChecksList::DEPRECATED_ENTITY_USAGE_CHECK_NAME
- . '".'
+ '--path option is not supported for the command."'
);
}
}
diff --git a/src/Magento/FunctionalTestingFramework/DataGenerator/Handlers/DataObjectHandler.php b/src/Magento/FunctionalTestingFramework/DataGenerator/Handlers/DataObjectHandler.php
index 5a4b42dc4..87dc6a13d 100644
--- a/src/Magento/FunctionalTestingFramework/DataGenerator/Handlers/DataObjectHandler.php
+++ b/src/Magento/FunctionalTestingFramework/DataGenerator/Handlers/DataObjectHandler.php
@@ -191,8 +191,8 @@ private function processParserOutput($parserOutput)
if (array_key_exists(self::OBJ_DEPRECATED, $rawEntity)) {
$deprecated = $rawEntity[self::OBJ_DEPRECATED];
LoggingUtil::getInstance()->getLogger(self::class)->deprecation(
- $deprecated,
- ["dataName" => $filename, "deprecatedEntity" => $deprecated]
+ "The data entity '{$name}' is deprecated.",
+ ["fileName" => $filename, "deprecatedMessage" => $deprecated]
);
}
diff --git a/src/Magento/FunctionalTestingFramework/DataGenerator/Handlers/OperationDefinitionObjectHandler.php b/src/Magento/FunctionalTestingFramework/DataGenerator/Handlers/OperationDefinitionObjectHandler.php
index 2c8ab7530..eb4651b3e 100644
--- a/src/Magento/FunctionalTestingFramework/DataGenerator/Handlers/OperationDefinitionObjectHandler.php
+++ b/src/Magento/FunctionalTestingFramework/DataGenerator/Handlers/OperationDefinitionObjectHandler.php
@@ -215,8 +215,8 @@ private function initialize()
if ($deprecated !== null) {
LoggingUtil::getInstance()->getLogger(self::class)->deprecation(
- $deprecated,
- ["operationName" => $dataDefName, "deprecatedOperation" => $deprecated]
+ $message = "The operation {$dataDefName} is deprecated.",
+ ["operationType" => $operation, "deprecatedMessage" => $deprecated]
);
}
diff --git a/src/Magento/FunctionalTestingFramework/DataGenerator/Objects/OperationDefinitionObject.php b/src/Magento/FunctionalTestingFramework/DataGenerator/Objects/OperationDefinitionObject.php
index 67fe48300..ef1a7faa5 100644
--- a/src/Magento/FunctionalTestingFramework/DataGenerator/Objects/OperationDefinitionObject.php
+++ b/src/Magento/FunctionalTestingFramework/DataGenerator/Objects/OperationDefinitionObject.php
@@ -6,6 +6,8 @@
namespace Magento\FunctionalTestingFramework\DataGenerator\Objects;
+use Magento\FunctionalTestingFramework\Util\Logger\LoggingUtil;
+
/**
* Class OperationDefinitionObject
* @SuppressWarnings(PHPMD)
@@ -341,4 +343,20 @@ public function addQueryParams()
$this->apiUrl = $this->apiUrl . $paramName . "=" . $paramValue;
}
}
+
+ /**
+ * Function to log a referenced deprecated operation at runtime.
+ *
+ * @return void
+ */
+ public function logDeprecated()
+ {
+ if ($this->deprecated != null) {
+ LoggingUtil::getInstance()->getLogger(self::class)->deprecation(
+ $message = "The operation {$this->name} is deprecated.",
+ ["operationType" => $this->operation, "deprecatedMessage" => $this->deprecated],
+ true
+ );
+ }
+ }
}
diff --git a/src/Magento/FunctionalTestingFramework/DataGenerator/Persist/CurlHandler.php b/src/Magento/FunctionalTestingFramework/DataGenerator/Persist/CurlHandler.php
index 362d25e75..89055b83f 100644
--- a/src/Magento/FunctionalTestingFramework/DataGenerator/Persist/CurlHandler.php
+++ b/src/Magento/FunctionalTestingFramework/DataGenerator/Persist/CurlHandler.php
@@ -123,6 +123,7 @@ public function executeRequest($dependentEntities)
$returnRegex = $this->operationDefinition->getReturnRegex();
$returnIndex = $this->operationDefinition->getReturnIndex();
$method = $this->operationDefinition->getApiMethod();
+ $this->operationDefinition->logDeprecated();
AllureHelper::addAttachmentToCurrentStep($apiUrl, 'API Endpoint');
AllureHelper::addAttachmentToCurrentStep(json_encode($headers, JSON_PRETTY_PRINT), 'Request Headers');
diff --git a/src/Magento/FunctionalTestingFramework/Module/MagentoActionProxies.php b/src/Magento/FunctionalTestingFramework/Module/MagentoActionProxies.php
index 5a3b2360b..55b19a7c1 100644
--- a/src/Magento/FunctionalTestingFramework/Module/MagentoActionProxies.php
+++ b/src/Magento/FunctionalTestingFramework/Module/MagentoActionProxies.php
@@ -20,5 +20,122 @@
*/
class MagentoActionProxies extends CodeceptionModule
{
- // TODO: placeholder for proxy functions currently in MagentoWebDriver (MQE-1904)
+ /**
+ * Create an entity
+ *
+ * @param string $key StepKey of the createData action.
+ * @param string $scope
+ * @param string $entity Name of xml entity to create.
+ * @param array $dependentObjectKeys StepKeys of other createData actions that are required.
+ * @param array $overrideFields Array of FieldName => Value of override fields.
+ * @param string $storeCode
+ * @return void
+ */
+ public function createEntity(
+ $key,
+ $scope,
+ $entity,
+ $dependentObjectKeys = [],
+ $overrideFields = [],
+ $storeCode = ''
+ ) {
+ PersistedObjectHandler::getInstance()->createEntity(
+ $key,
+ $scope,
+ $entity,
+ $dependentObjectKeys,
+ $overrideFields,
+ $storeCode
+ );
+ }
+
+ /**
+ * Retrieves and updates a previously created entity
+ *
+ * @param string $key StepKey of the createData action.
+ * @param string $scope
+ * @param string $updateEntity Name of the static XML data to update the entity with.
+ * @param array $dependentObjectKeys StepKeys of other createData actions that are required.
+ * @return void
+ */
+ public function updateEntity($key, $scope, $updateEntity, $dependentObjectKeys = [])
+ {
+ PersistedObjectHandler::getInstance()->updateEntity(
+ $key,
+ $scope,
+ $updateEntity,
+ $dependentObjectKeys
+ );
+ }
+
+ /**
+ * Performs GET on given entity and stores entity for use
+ *
+ * @param string $key StepKey of getData action.
+ * @param string $scope
+ * @param string $entity Name of XML static data to use.
+ * @param array $dependentObjectKeys StepKeys of other createData actions that are required.
+ * @param string $storeCode
+ * @param integer $index
+ * @return void
+ */
+ public function getEntity($key, $scope, $entity, $dependentObjectKeys = [], $storeCode = '', $index = null)
+ {
+ PersistedObjectHandler::getInstance()->getEntity(
+ $key,
+ $scope,
+ $entity,
+ $dependentObjectKeys,
+ $storeCode,
+ $index
+ );
+ }
+
+ /**
+ * Retrieves and deletes a previously created entity
+ *
+ * @param string $key StepKey of the createData action.
+ * @param string $scope
+ * @return void
+ */
+ public function deleteEntity($key, $scope)
+ {
+ PersistedObjectHandler::getInstance()->deleteEntity($key, $scope);
+ }
+
+ /**
+ * Retrieves a field from an entity, according to key and scope given
+ *
+ * @param string $stepKey
+ * @param string $field
+ * @param string $scope
+ * @return string
+ */
+ public function retrieveEntityField($stepKey, $field, $scope)
+ {
+ return PersistedObjectHandler::getInstance()->retrieveEntityField($stepKey, $field, $scope);
+ }
+
+ /**
+ * Get encrypted value by key
+ *
+ * @param string $key
+ * @return string|null
+ * @throws TestFrameworkException
+ */
+ public function getSecret($key)
+ {
+ return CredentialStore::getInstance()->getSecret($key);
+ }
+
+ /**
+ * Returns a value to origin of the action
+ *
+ * @param mixed $value
+ * @return mixed
+ */
+ public function return($value)
+ {
+ return $value;
+ }
}
diff --git a/src/Magento/FunctionalTestingFramework/Module/MagentoPwaWebDriver.php b/src/Magento/FunctionalTestingFramework/Module/MagentoPwaWebDriver.php
index 9b75cb10d..272f4206d 100644
--- a/src/Magento/FunctionalTestingFramework/Module/MagentoPwaWebDriver.php
+++ b/src/Magento/FunctionalTestingFramework/Module/MagentoPwaWebDriver.php
@@ -17,37 +17,57 @@
*/
class MagentoPwaWebDriver extends MagentoWebDriver
{
+ /**
+ * List of known PWA loading masks by selector
+ *
+ * Overriding the MagentoWebDriver array to contain applicable PWA locators.
+ *
+ * @var array
+ */
+ protected $loadingMasksLocators = [
+ '//div[contains(@class, "indicator-global-")]',
+ '//div[contains(@class, "indicator-root-")]',
+ '//img[contains(@class, "indicator-indicator-")]',
+ '//span[contains(@class, "indicator-message-")]'
+ ];
+
/**
* Go to the page.
*
* Overriding the MagentoWebDriver version because it contains 'waitForPageLoad'.
* The AJAX check in 'waitForPageLoad' does NOT work with a PWA.
*
- * @param string $page
+ * @param string $page
+ * @param integer $timeout
* @throws \Exception
* @return void
*/
- public function amOnPage($page)
+ public function amOnPage($page, $timeout = null)
{
WebDriver::amOnPage($page);
+ $this->waitForLoadingMaskToDisappear($timeout);
}
/**
* Wait for a PWA Element to NOT be visible using JavaScript.
* Add the WAIT_TIMEOUT variable to your .env file for this action.
*
- * @param null $selector
- * @param null $timeout
+ * @param string $selector
+ * @param integer $timeout
* @throws \Exception
* @return void
*/
public function waitForPwaElementNotVisible($selector, $timeout = null)
{
+ $timeout = $timeout ?? $this->_getConfig()['pageload_timeout'];
+
// Determine what type of Selector is used.
// Then use the correct JavaScript to locate the Element.
if (\Codeception\Util\Locator::isXPath($selector)) {
+ $this->waitForLoadingMaskToDisappear($timeout);
$this->waitForJS("return !document.evaluate(`$selector`, document);", $timeout);
} else {
+ $this->waitForLoadingMaskToDisappear($timeout);
$this->waitForJS("return !document.querySelector(`$selector`);", $timeout);
}
}
@@ -56,18 +76,22 @@ public function waitForPwaElementNotVisible($selector, $timeout = null)
* Wait for a PWA Element to be visible using JavaScript.
* Add the WAIT_TIMEOUT variable to your .env file for this action.
*
- * @param null $selector
- * @param null $timeout
+ * @param string $selector
+ * @param integer $timeout
* @throws \Exception
* @return void
*/
public function waitForPwaElementVisible($selector, $timeout = null)
{
+ $timeout = $timeout ?? $this->_getConfig()['pageload_timeout'];
+
// Determine what type of Selector is used.
// Then use the correct JavaScript to locate the Element.
if (\Codeception\Util\Locator::isXPath($selector)) {
+ $this->waitForLoadingMaskToDisappear($timeout);
$this->waitForJS("return !!document && !!document.evaluate(`$selector`, document);", $timeout);
} else {
+ $this->waitForLoadingMaskToDisappear($timeout);
$this->waitForJS("return !!document && !!document.querySelector(`$selector`);", $timeout);
}
}
diff --git a/src/Magento/FunctionalTestingFramework/Module/MagentoWebDriver.php b/src/Magento/FunctionalTestingFramework/Module/MagentoWebDriver.php
index 984881e4e..00e295606 100644
--- a/src/Magento/FunctionalTestingFramework/Module/MagentoWebDriver.php
+++ b/src/Magento/FunctionalTestingFramework/Module/MagentoWebDriver.php
@@ -15,6 +15,7 @@
use Codeception\Exception\ModuleConfigException;
use Codeception\Exception\ModuleException;
use Codeception\Util\Uri;
+use Codeception\Lib\ModuleContainer;
use Magento\FunctionalTestingFramework\DataTransport\WebApiExecutor;
use Magento\FunctionalTestingFramework\DataTransport\Auth\WebApiAuth;
use Magento\FunctionalTestingFramework\DataTransport\Auth\Tfa\OTP;
@@ -53,7 +54,9 @@
class MagentoWebDriver extends WebDriver
{
use AttachmentSupport;
- use Pause;
+ use Pause {
+ pause as codeceptPause;
+ }
const MAGENTO_CRON_INTERVAL = 60;
const MAGENTO_CRON_COMMAND = 'cron:run';
@@ -63,7 +66,7 @@ class MagentoWebDriver extends WebDriver
*
* @var array
*/
- public static $loadingMasksLocators = [
+ protected $loadingMasksLocators = [
'//div[contains(@class, "loading-mask")]',
'//div[contains(@class, "admin_data-grid-loading-mask")]',
'//div[contains(@class, "admin__data-grid-loading-mask")]',
@@ -180,6 +183,16 @@ public function _after(TestInterface $test)
// DO NOT RESET SESSIONS
}
+ /**
+ * Return ModuleContainer
+ *
+ * @return ModuleContainer
+ */
+ public function getModuleContainer()
+ {
+ return $this->moduleContainer;
+ }
+
/**
* Returns URL of a host.
*
@@ -439,7 +452,9 @@ public function waitForPageLoad($timeout = null)
*/
public function waitForLoadingMaskToDisappear($timeout = null)
{
- foreach (self::$loadingMasksLocators as $maskLocator) {
+ $timeout = $timeout ?? $this->_getConfig()['pageload_timeout'];
+
+ foreach ($this->loadingMasksLocators as $maskLocator) {
// Get count of elements found for looping.
// Elements are NOT useful for interaction, as they cannot be fed to codeception actions.
$loadingMaskElements = $this->_findElements($maskLocator);
@@ -748,19 +763,25 @@ public function dragAndDrop($source, $target, $xOffset = null, $yOffset = null)
$snodes = $this->matchFirstOrFail($this->baseElement, $source);
$tnodes = $this->matchFirstOrFail($this->baseElement, $target);
$action = new WebDriverActions($this->webDriver);
-
if ($xOffset !== null || $yOffset !== null) {
$targetX = intval($tnodes->getLocation()->getX() + $xOffset);
$targetY = intval($tnodes->getLocation()->getY() + $yOffset);
-
$travelX = intval($targetX - $snodes->getLocation()->getX());
$travelY = intval($targetY - $snodes->getLocation()->getY());
$action->moveToElement($snodes);
$action->clickAndHold($snodes);
+ // Fix Start
+ $action->moveByOffset(-1, -1);
+ $action->moveByOffset(1, 1);
+ // Fix End
$action->moveByOffset($travelX, $travelY);
$action->release()->perform();
} else {
$action->clickAndHold($snodes);
+ // Fix Start
+ $action->moveByOffset(-1, -1);
+ $action->moveByOffset(1, 1);
+ // Fix End
$action->moveToElement($tnodes);
$action->release($tnodes)->perform();
}
@@ -823,6 +844,9 @@ public function _failed(TestInterface $test, $fail)
if ($this->pngReport === null && $this->htmlReport === null) {
$this->saveScreenshot();
+ if (getenv('ENABLE_PAUSE') === 'true') {
+ $this->pause(true);
+ }
}
if ($this->current_test == null) {
@@ -864,7 +888,7 @@ public function saveScreenshot()
*/
public function amOnPage($page)
{
- parent::amOnPage($page);
+ (0 === strpos($page, 'http')) ? parent::amOnUrl($page) : parent::amOnPage($page);
$this->waitForPageLoad();
}
@@ -954,120 +978,6 @@ public function getOTP()
return OTP::getOTP();
}
- /**
- * Create an entity
- * TODO: move this function to MagentoActionProxies after MQE-1904
- *
- * @param string $key StepKey of the createData action.
- * @param string $scope
- * @param string $entity Name of xml entity to create.
- * @param array $dependentObjectKeys StepKeys of other createData actions that are required.
- * @param array $overrideFields Array of FieldName => Value of override fields.
- * @param string $storeCode
- * @return void
- */
- public function createEntity(
- $key,
- $scope,
- $entity,
- $dependentObjectKeys = [],
- $overrideFields = [],
- $storeCode = ''
- ) {
- PersistedObjectHandler::getInstance()->createEntity(
- $key,
- $scope,
- $entity,
- $dependentObjectKeys,
- $overrideFields,
- $storeCode
- );
- }
-
- /**
- * Retrieves and updates a previously created entity
- * TODO: move this function to MagentoActionProxies after MQE-1904
- *
- * @param string $key StepKey of the createData action.
- * @param string $scope
- * @param string $updateEntity Name of the static XML data to update the entity with.
- * @param array $dependentObjectKeys StepKeys of other createData actions that are required.
- * @return void
- */
- public function updateEntity($key, $scope, $updateEntity, $dependentObjectKeys = [])
- {
- PersistedObjectHandler::getInstance()->updateEntity(
- $key,
- $scope,
- $updateEntity,
- $dependentObjectKeys
- );
- }
-
- /**
- * Performs GET on given entity and stores entity for use
- * TODO: move this function to MagentoActionProxies after MQE-1904
- *
- * @param string $key StepKey of getData action.
- * @param string $scope
- * @param string $entity Name of XML static data to use.
- * @param array $dependentObjectKeys StepKeys of other createData actions that are required.
- * @param string $storeCode
- * @param integer $index
- * @return void
- */
- public function getEntity($key, $scope, $entity, $dependentObjectKeys = [], $storeCode = '', $index = null)
- {
- PersistedObjectHandler::getInstance()->getEntity(
- $key,
- $scope,
- $entity,
- $dependentObjectKeys,
- $storeCode,
- $index
- );
- }
-
- /**
- * Retrieves and deletes a previously created entity
- * TODO: move this function to MagentoActionProxies after MQE-1904
- *
- * @param string $key StepKey of the createData action.
- * @param string $scope
- * @return void
- */
- public function deleteEntity($key, $scope)
- {
- PersistedObjectHandler::getInstance()->deleteEntity($key, $scope);
- }
-
- /**
- * Retrieves a field from an entity, according to key and scope given
- * TODO: move this function to MagentoActionProxies after MQE-1904
- *
- * @param string $stepKey
- * @param string $field
- * @param string $scope
- * @return string
- */
- public function retrieveEntityField($stepKey, $field, $scope)
- {
- return PersistedObjectHandler::getInstance()->retrieveEntityField($stepKey, $field, $scope);
- }
-
- /**
- * Get encrypted value by key
- * TODO: move this function to MagentoActionProxies after MQE-1904
- *
- * @param string $key
- * @return string|null
- * @throws TestFrameworkException
- */
- public function getSecret($key)
- {
- return CredentialStore::getInstance()->getSecret($key);
- }
-
/**
* Waits proper amount of time to perform Cron execution
*
@@ -1120,4 +1030,23 @@ public function switchToIFrame($locator = null)
$this->webDriver->switchTo()->frame($els[0]);
}
}
+
+ /**
+ * Invoke Codeption pause()
+ *
+ * @param boolean $pauseOnFail
+ * @return void
+ */
+ public function pause($pauseOnFail = false)
+ {
+ if (!\Codeception\Util\Debug::isEnabled()) {
+ return;
+ }
+
+ if ($pauseOnFail) {
+ print(PHP_EOL . "Failure encountered. Pausing execution..." . PHP_EOL . PHP_EOL);
+ }
+
+ $this->codeceptPause();
+ }
}
diff --git a/src/Magento/FunctionalTestingFramework/Page/Handlers/PageObjectHandler.php b/src/Magento/FunctionalTestingFramework/Page/Handlers/PageObjectHandler.php
index 73695c182..8f4302077 100644
--- a/src/Magento/FunctionalTestingFramework/Page/Handlers/PageObjectHandler.php
+++ b/src/Magento/FunctionalTestingFramework/Page/Handlers/PageObjectHandler.php
@@ -79,8 +79,8 @@ private function __construct()
if ($deprecated !== null) {
LoggingUtil::getInstance()->getLogger(self::class)->deprecation(
- $deprecated,
- ["pageName" => $filename, "deprecatedPage" => $deprecated]
+ "The page '{$pageName}' is deprecated.",
+ ["fileName" => $filename, "deprecatedMessage" => $deprecated]
);
}
diff --git a/src/Magento/FunctionalTestingFramework/Page/Handlers/SectionObjectHandler.php b/src/Magento/FunctionalTestingFramework/Page/Handlers/SectionObjectHandler.php
index 5d658bac0..6ac3456fd 100644
--- a/src/Magento/FunctionalTestingFramework/Page/Handlers/SectionObjectHandler.php
+++ b/src/Magento/FunctionalTestingFramework/Page/Handlers/SectionObjectHandler.php
@@ -89,8 +89,8 @@ private function __construct()
$elementDeprecated = $elementData[self::OBJ_DEPRECATED] ?? null;
if ($elementDeprecated !== null) {
LoggingUtil::getInstance()->getLogger(ElementObject::class)->deprecation(
- $elementDeprecated,
- ["elementName" => $elementName, "deprecatedElement" => $elementDeprecated]
+ "The element '{$elementName}' is deprecated.",
+ ["fileName" => $filename, "deprecatedMessage" => $elementDeprecated]
);
}
$elements[$elementName] = new ElementObject(
diff --git a/src/Magento/FunctionalTestingFramework/StaticCheck/DeprecatedEntityUsageCheck.php b/src/Magento/FunctionalTestingFramework/StaticCheck/DeprecatedEntityUsageCheck.php
index 5da16ec6a..547e6a633 100644
--- a/src/Magento/FunctionalTestingFramework/StaticCheck/DeprecatedEntityUsageCheck.php
+++ b/src/Magento/FunctionalTestingFramework/StaticCheck/DeprecatedEntityUsageCheck.php
@@ -37,6 +37,7 @@ class DeprecatedEntityUsageCheck implements StaticCheckInterface
{
const EXTENDS_REGEX_PATTERN = '/extends=["\']([^\'"]*)/';
const ACTIONGROUP_REGEX_PATTERN = '/ref=["\']([^\'"]*)/';
+ const DEPRECATED_REGEX_PATTERN = '/deprecated=["\']([^\'"]*)/';
const ERROR_LOG_FILENAME = 'mftf-deprecated-entity-usage-checks';
const ERROR_LOG_MESSAGE = 'MFTF Deprecated Entity Usage Check';
@@ -177,7 +178,6 @@ private function loadAllXmlFiles($input)
MftfApplicationConfig::LEVEL_DEFAULT,
true
);
- putenv('CUSTOM_MODULE_PATHS=' . realpath($path));
$modulePaths[] = realpath($path);
$includeRootPath = false;
} else {
@@ -225,6 +225,9 @@ private function findReferenceErrorsInActionFiles($files, $checkTestRef = false)
/** @var SplFileInfo $filePath */
foreach ($files as $filePath) {
$contents = file_get_contents($filePath);
+ if ($this->isDeprecated($contents)) {
+ continue;
+ }
preg_match_all(ActionObject::ACTION_ATTRIBUTE_VARIABLE_REGEX_PATTERN, $contents, $braceReferences);
preg_match_all(self::ACTIONGROUP_REGEX_PATTERN, $contents, $actionGroupReferences);
preg_match_all(self::EXTENDS_REGEX_PATTERN, $contents, $extendReferences);
@@ -316,6 +319,17 @@ private function findReferenceErrorsInActionFiles($files, $checkTestRef = false)
return $testErrors;
}
+ /**
+ * Checks if entity is deprecated in action files.
+ * @param string $contents
+ * @return boolean
+ */
+ private function isDeprecated($contents)
+ {
+ preg_match_all(self::DEPRECATED_REGEX_PATTERN, $contents, $deprecatedEntity);
+ return (!empty($deprecatedEntity[1]));
+ }
+
/**
* Find reference errors in a set of data files
*
@@ -335,6 +349,11 @@ private function findReferenceErrorsInDataFiles($files)
$entities = $domDocument->getElementsByTagName('entity');
foreach ($entities as $entity) {
/** @var DOMElement $entity */
+ $deprecated = $entity->getAttribute('deprecated');
+ // skip check if entity is deprecated
+ if (!empty($deprecated)) {
+ continue;
+ }
$entityName = $entity->getAttribute('name');
$metadataType = $entity->getAttribute('type');
$parentEntityName = $entity->getAttribute('extends');
@@ -609,9 +628,9 @@ private function findViolatingReferences($references)
$name = $key;
list($section,) = explode('.', $key, 2);
/** @var SectionObject $references[$section] */
- $file = $references[$section]->getFilename();
+ $file = StaticChecksList::getFilePath($references[$section]->getFilename());
} else {
- $file = $entity->getFilename();
+ $file = StaticChecksList::getFilePath($entity->getFilename());
}
$violatingReferences[$this->getSubjectFromClassType($classType)][] = [
'name' => $name,
@@ -633,16 +652,18 @@ private function setErrorOutput($violatingReferences, $path)
{
$testErrors = [];
+ $filePath = StaticChecksList::getFilePath($path->getRealPath());
+
if (!empty($violatingReferences)) {
// Build error output
- $errorOutput = "\nFile \"{$path->getRealPath()}\" contains:\n";
+ $errorOutput = "\nFile \"{$filePath}\" contains:\n";
foreach ($violatingReferences as $subject => $data) {
$errorOutput .= "\t- {$subject}:\n";
foreach ($data as $item) {
$errorOutput .= "\t\t\"" . $item['name'] . "\" in " . $item['file'] . "\n";
}
}
- $testErrors[$path->getRealPath()][] = $errorOutput;
+ $testErrors[$filePath][] = $errorOutput;
}
return $testErrors;
diff --git a/src/Magento/FunctionalTestingFramework/StaticCheck/PauseActionUsageCheck.php b/src/Magento/FunctionalTestingFramework/StaticCheck/PauseActionUsageCheck.php
new file mode 100644
index 000000000..e7d106bf0
--- /dev/null
+++ b/src/Magento/FunctionalTestingFramework/StaticCheck/PauseActionUsageCheck.php
@@ -0,0 +1,229 @@
+scriptUtil = new ScriptUtil();
+ $modulePaths = [];
+ $includeRootPath = true;
+ $path = $input->getOption('path');
+ if ($path) {
+ if (!realpath($path)) {
+ throw new \InvalidArgumentException('Invalid --path option: ' . $path);
+ }
+ $modulePaths[] = realpath($path);
+ $includeRootPath = false;
+ } else {
+ $modulePaths = $this->scriptUtil->getAllModulePaths();
+ }
+
+ $this->testXmlFiles = $this->scriptUtil->getModuleXmlFilesByScope($modulePaths, 'Test');
+ $this->actionGroupXmlFiles = $this->scriptUtil->getModuleXmlFilesByScope($modulePaths, 'ActionGroup');
+ $this->suiteXmlFiles = $this->scriptUtil->getModuleXmlFilesByScope($modulePaths, 'Suite');
+ if ($includeRootPath) {
+ $this->rootSuiteXmlFiles = $this->scriptUtil->getRootSuiteXmlFiles();
+ }
+ $this->errors = [];
+ $this->errors += $this->validatePauseActionUsageInActionGroups($this->actionGroupXmlFiles);
+ $this->errors += $this->validatePauseActionUsageInTests($this->testXmlFiles);
+ $this->errors += $this->validatePauseActionUsageInSuites($this->suiteXmlFiles);
+ $this->errors += $this->validatePauseActionUsageInSuites($this->rootSuiteXmlFiles);
+
+ $this->output = $this->scriptUtil->printErrorsToFile(
+ $this->errors,
+ StaticChecksList::getErrorFilesPath() . DIRECTORY_SEPARATOR . self::ERROR_LOG_FILENAME . '.txt',
+ self::ERROR_LOG_MESSAGE
+ );
+ }
+
+ /**
+ * Finds usages of pause action in action group files
+ * @param array $actionGroupXmlFiles
+ * @return array
+ */
+ private function validatePauseActionUsageInActionGroups($actionGroupXmlFiles)
+ {
+ $actionGroupErrors = [];
+ foreach ($actionGroupXmlFiles as $filePath) {
+ $domDocument = new \DOMDocument();
+ $domDocument->load($filePath);
+ $actionGroup = $domDocument->getElementsByTagName('actionGroup')->item(0);
+ $violatingStepKeys = $this->findViolatingPauseStepKeys($actionGroup);
+ $actionGroupErrors = array_merge($actionGroupErrors, $this->setErrorOutput($violatingStepKeys, $filePath));
+ }
+ return $actionGroupErrors;
+ }
+
+ /**
+ * Finds usages of pause action in test files
+ * @param array $testXmlFiles
+ * @return array
+ */
+ private function validatePauseActionUsageInTests($testXmlFiles)
+ {
+ $testErrors = [];
+ foreach ($testXmlFiles as $filePath) {
+ $domDocument = new \DOMDocument();
+ $domDocument->load($filePath);
+ $test = $domDocument->getElementsByTagName('test')->item(0);
+ $violatingStepKeys = $this->findViolatingPauseStepKeys($test);
+ $testErrors = array_merge($testErrors, $this->setErrorOutput($violatingStepKeys, $filePath));
+ }
+ return $testErrors;
+ }
+
+ /**
+ * Finds usages of pause action in suite files
+ * @param array $suiteXmlFiles
+ * @return array
+ */
+ private function validatePauseActionUsageInSuites($suiteXmlFiles)
+ {
+ $suiteErrors = [];
+ foreach ($suiteXmlFiles as $filePath) {
+ $domDocument = new \DOMDocument();
+ $domDocument->load($filePath);
+ $suite = $domDocument->getElementsByTagName('suite')->item(0);
+ $violatingStepKeys = $this->findViolatingPauseStepKeys($suite);
+ $suiteErrors = array_merge($suiteErrors, $this->setErrorOutput($violatingStepKeys, $filePath));
+ }
+ return $suiteErrors;
+ }
+
+ /**
+ * Finds violating pause action step keys
+ * @param \DomNode $entity
+ * @return array
+ */
+ private function findViolatingPauseStepKeys($entity)
+ {
+ $violatingStepKeys = [];
+ $entityName = $entity->getAttribute('name');
+ $references = $entity->getElementsByTagName('pause');
+
+ foreach ($references as $reference) {
+ $pauseStepKey = $reference->getAttribute('stepKey');
+ $violatingStepKeys[$entityName][] = $pauseStepKey;
+ }
+ return $violatingStepKeys;
+ }
+
+ /**
+ * Return array containing all errors found after running the execute() function.
+ * @return array
+ */
+ public function getErrors()
+ {
+ return $this->errors;
+ }
+
+ /**
+ * Return string of a short human readable result of the check. For example: "No errors found."
+ * @return string
+ */
+ public function getOutput()
+ {
+ return $this->output;
+ }
+
+ /**
+ * Build and return error output for pause action usages
+ *
+ * @param array $violatingReferences
+ * @param SplFileInfo $path
+ * @return mixed
+ */
+ private function setErrorOutput($violatingReferences, $path)
+ {
+ $testErrors = [];
+
+ $filePath = StaticChecksList::getFilePath($path->getRealPath());
+
+ if (!empty($violatingReferences)) {
+ // Build error output
+ $errorOutput = "\nFile \"{$filePath}\"";
+ $errorOutput .= "\ncontains pause action(s):\n\t\t";
+ foreach ($violatingReferences as $entityName => $stepKey) {
+ $errorOutput .= "\n\t {$entityName} has pause action at stepKey(s): " . implode(", ", $stepKey);
+ }
+ $testErrors[$filePath][] = $errorOutput;
+ }
+ return $testErrors;
+ }
+}
diff --git a/src/Magento/FunctionalTestingFramework/StaticCheck/StaticChecksList.php b/src/Magento/FunctionalTestingFramework/StaticCheck/StaticChecksList.php
index 7cd894e00..07c52ce8c 100644
--- a/src/Magento/FunctionalTestingFramework/StaticCheck/StaticChecksList.php
+++ b/src/Magento/FunctionalTestingFramework/StaticCheck/StaticChecksList.php
@@ -17,6 +17,7 @@
class StaticChecksList implements StaticCheckListInterface
{
const DEPRECATED_ENTITY_USAGE_CHECK_NAME = 'deprecatedEntityUsage';
+ const PAUSE_ACTION_USAGE_CHECK_NAME = 'pauseActionUsage';
const STATIC_RESULTS = 'tests' . DIRECTORY_SEPARATOR .'_output' . DIRECTORY_SEPARATOR . 'static-results';
/**
@@ -45,7 +46,8 @@ public function __construct(array $checks = [])
'testDependencies' => new TestDependencyCheck(),
'actionGroupArguments' => new ActionGroupArgumentsCheck(),
self::DEPRECATED_ENTITY_USAGE_CHECK_NAME => new DeprecatedEntityUsageCheck(),
- 'annotations' => new AnnotationsCheck()
+ 'annotations' => new AnnotationsCheck(),
+ self::PAUSE_ACTION_USAGE_CHECK_NAME => new PauseActionUsageCheck()
] + $checks;
// Static checks error files directory
@@ -69,4 +71,22 @@ public static function getErrorFilesPath()
{
return self::$errorFilesPath;
}
+
+ /**
+ * Return relative path to files for unit testing purposes.
+ * @param string $fileNames
+ * @return string
+ */
+ public static function getFilePath($fileNames)
+ {
+ if (!empty($fileNames)) {
+ $relativeFileNames = ltrim(
+ str_replace(MAGENTO_BP, '', $fileNames)
+ );
+ if (!empty($relativeFileNames)) {
+ return $relativeFileNames;
+ }
+ }
+ return $fileNames;
+ }
}
diff --git a/src/Magento/FunctionalTestingFramework/Suite/Generators/GroupClassGenerator.php b/src/Magento/FunctionalTestingFramework/Suite/Generators/GroupClassGenerator.php
index 67930f09a..f90a25322 100644
--- a/src/Magento/FunctionalTestingFramework/Suite/Generators/GroupClassGenerator.php
+++ b/src/Magento/FunctionalTestingFramework/Suite/Generators/GroupClassGenerator.php
@@ -6,6 +6,7 @@
namespace Magento\FunctionalTestingFramework\Suite\Generators;
+use Magento\FunctionalTestingFramework\Exceptions\TestFrameworkException;
use Magento\FunctionalTestingFramework\Exceptions\TestReferenceException;
use Magento\FunctionalTestingFramework\Suite\Objects\SuiteObject;
use Magento\FunctionalTestingFramework\Test\Objects\ActionObject;
@@ -32,6 +33,10 @@ class GroupClassGenerator
'comment' => 'print'
];
const GROUP_DIR_NAME = 'Group';
+ const FUNCTION_PLACEHOLDER = 'PLACEHOLDER';
+ const FUNCTION_START = 'this->getModuleForAction("';
+ const FUNCTION_END = '")';
+ const FUNCTION_REPLACE_REGEX = '/(PLACEHOLDER->([^\(]+))\(/';
/**
* Mustache_Engine instance for template loading
@@ -142,7 +147,7 @@ private function buildHookMustacheArray($hookObj)
//deleteData contains either url or createDataKey, if it contains the former it needs special formatting
if ($action->getType() !== "createData"
&& !array_key_exists(TestGenerator::REQUIRED_ENTITY_REFERENCE, $action->getCustomActionAttributes())) {
- $actions = $this->buildWebDriverActionsMustacheArray($action, $actions);
+ $actions = $this->buildModuleActionsMustacheArray($action, $actions);
continue;
}
@@ -165,7 +170,7 @@ private function buildHookMustacheArray($hookObj)
}
/**
- * Takes an action object and array of generated action steps. Converst the action object into generated php and
+ * Takes an action object and array of generated action steps. Convert the action object into generated php and
* appends the entry to the given array. The result is returned by the function.
*
* @param ActionObject $action
@@ -173,14 +178,22 @@ private function buildHookMustacheArray($hookObj)
* @return array
* @throws TestReferenceException
*/
- private function buildWebDriverActionsMustacheArray($action, $actionEntries)
+ private function buildModuleActionsMustacheArray($action, $actionEntries)
{
- $step = TestGenerator::getInstance()->generateStepsPhp([$action], TestGenerator::SUITE_SCOPE, 'webDriver');
+ $step = TestGenerator::getInstance()->generateStepsPhp(
+ [$action],
+ TestGenerator::SUITE_SCOPE,
+ self::FUNCTION_PLACEHOLDER
+ );
$rawPhp = str_replace(["\t"], "", $step);
$multipleCommands = explode(PHP_EOL, $rawPhp, -1);
$multipleCommands = array_filter($multipleCommands);
foreach ($multipleCommands as $command) {
- $actionEntries = $this->replaceReservedTesterFunctions($command . PHP_EOL, $actionEntries, 'webDriver');
+ $actionEntries = $this->replaceReservedTesterFunctions(
+ $command . PHP_EOL,
+ $actionEntries,
+ self::FUNCTION_PLACEHOLDER
+ );
}
return $actionEntries;
@@ -204,7 +217,17 @@ private function replaceReservedTesterFunctions($formattedStep, $actionEntries,
$resultingStep = str_replace($testActionCall, $replacement, $formattedStep);
$actionEntries[] = ['action' => $resultingStep];
} else {
- $actionEntries[] = ['action' => $formattedStep];
+ $placeholder = self::FUNCTION_PLACEHOLDER;
+ $begin = self::FUNCTION_START;
+ $end = self::FUNCTION_END;
+ $resultingStep = preg_replace_callback(
+ self::FUNCTION_REPLACE_REGEX,
+ function ($matches) use ($placeholder, $begin, $end) {
+ return str_replace($placeholder, $begin . $matches[2] . $end, $matches[1]) . '(';
+ },
+ $formattedStep
+ );
+ $actionEntries[] = ['action' => $resultingStep];
}
}
@@ -225,16 +248,16 @@ private function buildPersistenceMustacheArray($action, $entityArray)
$action->getCustomActionAttributes()[TestGenerator::REQUIRED_ENTITY_REFERENCE];
// append entries for any required entities to this entry
- if (array_key_exists('requiredEntities', $action->getCustomActionAttributes())) {
- $entityArray[self::REQUIRED_ENTITY_KEY] =
- $this->buildReqEntitiesMustacheArray($action->getCustomActionAttributes());
+ $requiredEntities = $this->buildReqEntitiesMustacheArray($action->getCustomActionAttributes());
+ if (!array_key_exists(-1, $requiredEntities)) {
+ $entityArray[self::REQUIRED_ENTITY_KEY] = $requiredEntities;
}
// append entries for customFields if specified by the user.
if (array_key_exists('customFields', $action->getCustomActionAttributes())) {
$entityArray['customFields'] = $action->getStepKey() . 'Fields';
}
-
+
return $entityArray;
}
diff --git a/src/Magento/FunctionalTestingFramework/Suite/views/SuiteClass.mustache b/src/Magento/FunctionalTestingFramework/Suite/views/SuiteClass.mustache
index a9fdcd6df..dc2fde915 100644
--- a/src/Magento/FunctionalTestingFramework/Suite/views/SuiteClass.mustache
+++ b/src/Magento/FunctionalTestingFramework/Suite/views/SuiteClass.mustache
@@ -6,6 +6,11 @@ use Facebook\WebDriver\Remote\RemoteWebDriver;
use Magento\FunctionalTestingFramework\DataGenerator\Handlers\PersistedObjectHandler;
use Magento\FunctionalTestingFramework\DataGenerator\Handlers\CredentialStore;
use Magento\FunctionalTestingFramework\Module\MagentoWebDriver;
+use Magento\FunctionalTestingFramework\Module\MagentoAssert;
+use Magento\FunctionalTestingFramework\Module\MagentoActionProxies;
+use Magento\FunctionalTestingFramework\Exceptions\TestFrameworkException;
+use Codeception\Lib\ModuleContainer;
+use Codeception\Module;
/**
* Group class is Codeception Extension which is allowed to handle to all internal events.
@@ -30,10 +35,16 @@ class {{suiteName}} extends \Codeception\GroupObject
{{/helpers}}
private static $HOOK_EXECUTION_INIT = "\n/******** Beginning execution of {{suiteName}} suite %s block ********/\n";
private static $HOOK_EXECUTION_END = "\n/******** Execution of {{suiteName}} suite %s block complete ********/\n";
+ /** @var MagentoWebDriver */
+ private $webDriver;
+ /** @var ModuleContainer */
+ private $moduleContainer;
{{#before}}
public function _before(\Codeception\Event\TestEvent $e)
{
+ $this->webDriver = $this->getModule('\Magento\FunctionalTestingFramework\Module\MagentoWebDriver');
+ $this->moduleContainer = $this->webDriver->getModuleContainer();
{{#helpers}}
/** @var \Magento\FunctionalTestingFramework\Helper\HelperContainer $helperContainer */
$this->helperContainer = $this->getModule('\Magento\FunctionalTestingFramework\Helper\HelperContainer');
@@ -51,15 +62,11 @@ class {{suiteName}} extends \Codeception\GroupObject
}
}
-
private function executePreConditions()
{
if ($this->currentTestRun == 1) {
print sprintf(self::$HOOK_EXECUTION_INIT, "before");
- /** @var MagentoWebDriver $webDriver */
- $webDriver = $this->getModule('\Magento\FunctionalTestingFramework\Module\MagentoWebDriver');
-
try {
{{> testActions}}
} catch (\Exception $exception) {
@@ -67,9 +74,9 @@ class {{suiteName}} extends \Codeception\GroupObject
}
// reset configuration and close session
- $webDriver->_resetConfig();
- $webDriver->webDriver->close();
- $webDriver->webDriver = null;
+ $this->webDriver->_resetConfig();
+ $this->webDriver->webDriver->close();
+ $this->webDriver->webDriver = null;
print sprintf(self::$HOOK_EXECUTION_END, "before");
}
@@ -82,15 +89,11 @@ class {{suiteName}} extends \Codeception\GroupObject
$this->executePostConditions($e);
}
-
private function executePostConditions(\Codeception\Event\TestEvent $e)
{
if ($this->currentTestRun == $this->testCount) {
print sprintf(self::$HOOK_EXECUTION_INIT, "after");
- /** @var MagentoWebDriver $webDriver */
- $webDriver = $this->getModule('\Magento\FunctionalTestingFramework\Module\MagentoWebDriver');
-
try {
// Find out if Test in Suite failed, will cause potential failures in suite after
$cest = $e->getTest();
@@ -120,7 +123,7 @@ class {{suiteName}} extends \Codeception\GroupObject
PersistedObjectHandler::getInstance()->clearSuiteObjects();
- $this->closeSession($webDriver);
+ $this->closeSession($this->webDriver);
print sprintf(self::$HOOK_EXECUTION_END, "after");
}
@@ -131,13 +134,12 @@ class {{suiteName}} extends \Codeception\GroupObject
* Close session method closes current session.
* If config 'close_all_sessions' is set to 'true' all sessions will be closed.
*
- * @param MagentoWebDriver $webDriver
* return void
*/
- private function closeSession(MagentoWebDriver $webDriver): void
+ private function closeSession(): void
{
- $webDriverConfig = $webDriver->_getConfig();
- $webDriver->_closeSession();
+ $webDriverConfig = $this->webDriver->_getConfig();
+ $this->webDriver->_closeSession();
if (isset($webDriverConfig['close_all_sessions']) && $webDriverConfig['close_all_sessions'] === "true") {
$wdHost = sprintf(
'%s://%s:%s%s',
@@ -153,4 +155,20 @@ class {{suiteName}} extends \Codeception\GroupObject
}
}
}
+
+ /**
+ * Return the module for an action.
+ *
+ * @param string $action
+ * @return Module
+ * @throws \Exception
+ */
+ private function getModuleForAction($action)
+ {
+ $module = $this->moduleContainer->moduleForAction($action);
+ if ($module === null) {
+ throw new TestFrameworkException('Invalid action "' . $action . '"' . PHP_EOL);
+ }
+ return $module;
+ }
}
diff --git a/src/Magento/FunctionalTestingFramework/Suite/views/partials/testActions.mustache b/src/Magento/FunctionalTestingFramework/Suite/views/partials/testActions.mustache
index 73079f169..c07958778 100644
--- a/src/Magento/FunctionalTestingFramework/Suite/views/partials/testActions.mustache
+++ b/src/Magento/FunctionalTestingFramework/Suite/views/partials/testActions.mustache
@@ -1,9 +1,9 @@
{{#actions}}
{{#webDriverInit}}
-if ($webDriver->webDriver != null) {
- $webDriver->_restart();
+if ($this->webDriver->webDriver != null) {
+ $this->webDriver->_restart();
} else {
- $webDriver->_initializeSession();
+ $this->webDriver->_initializeSession();
}
{{/webDriverInit}}
{{#action}}
@@ -13,9 +13,9 @@ if ($webDriver->webDriver != null) {
PersistedObjectHandler::getInstance()->createEntity(
"{{stepKey}}",
"suite",
- "{{entityName}}"{{#requiredEntities}},
- [$this->{{entityName}}{{^last}}, {{/last}}]{{/requiredEntities}}{{#customFields}},
- ${{customFields}}{{/customFields}}
+ "{{entityName}}",
+ [{{#requiredEntities}}"{{entityName}}"{{^last}}, {{/last}}{{/requiredEntities}}]{{#customFields}},
+ ${{customFields}}{{/customFields}}
);
{{/createData}}
{{#deleteData}}
diff --git a/src/Magento/FunctionalTestingFramework/Test/Objects/ActionObject.php b/src/Magento/FunctionalTestingFramework/Test/Objects/ActionObject.php
index 648471c7a..8e4aa973c 100644
--- a/src/Magento/FunctionalTestingFramework/Test/Objects/ActionObject.php
+++ b/src/Magento/FunctionalTestingFramework/Test/Objects/ActionObject.php
@@ -77,6 +77,7 @@ class ActionObject
const ACTION_TYPE_COMMENT = 'comment';
const ACTION_TYPE_HELPER = 'helper';
const INVISIBLE_STEP_ACTIONS = ['retrieveEntityField', 'getSecret'];
+ const PAUSE_ACTION_INTERNAL_ATTRIBUTE = 'pauseOnFail';
/**
* The unique identifier for the action
@@ -726,7 +727,12 @@ private function resolveParameterization($isParameterized, $replacement, $match,
$resolvedReplacement = $replacement;
}
if (get_class($object) == PageObject::class && $object->getArea() == PageObject::ADMIN_AREA) {
- $resolvedReplacement = "/{{_ENV.MAGENTO_BACKEND_NAME}}/" . $resolvedReplacement;
+ $urlSegments = [
+ '{{_ENV.MAGENTO_BACKEND_BASE_URL}}',
+ '{{_ENV.MAGENTO_BACKEND_NAME}}',
+ $resolvedReplacement
+ ];
+ $resolvedReplacement = implode('/', $urlSegments);
}
return $resolvedReplacement;
}
diff --git a/src/Magento/FunctionalTestingFramework/Test/Util/ActionGroupObjectExtractor.php b/src/Magento/FunctionalTestingFramework/Test/Util/ActionGroupObjectExtractor.php
index 618cddc09..84b960f1b 100644
--- a/src/Magento/FunctionalTestingFramework/Test/Util/ActionGroupObjectExtractor.php
+++ b/src/Magento/FunctionalTestingFramework/Test/Util/ActionGroupObjectExtractor.php
@@ -65,8 +65,8 @@ public function extractActionGroup($actionGroupData)
if (array_key_exists(self::OBJ_DEPRECATED, $actionGroupData)) {
$deprecated = $actionGroupData[self::OBJ_DEPRECATED];
LoggingUtil::getInstance()->getLogger(ActionGroupObject::class)->deprecation(
- $deprecated,
- ["actionGroupName" => $actionGroupData[self::FILENAME], "deprecatedActionGroup" => $deprecated]
+ "The action group '{$actionGroupData[self::NAME]}' is deprecated.",
+ ["fileName" => $actionGroupData[self::FILENAME], "deprecatedMessage" => $deprecated]
);
}
$actionGroupReference = $actionGroupData[self::EXTENDS_ACTION_GROUP] ?? null;
diff --git a/src/Magento/FunctionalTestingFramework/Test/Util/TestHookObjectExtractor.php b/src/Magento/FunctionalTestingFramework/Test/Util/TestHookObjectExtractor.php
index c11a6e512..e9c163d31 100644
--- a/src/Magento/FunctionalTestingFramework/Test/Util/TestHookObjectExtractor.php
+++ b/src/Magento/FunctionalTestingFramework/Test/Util/TestHookObjectExtractor.php
@@ -57,19 +57,26 @@ public function extractHook($parentName, $hookType, $testHook)
/**
* Creates the default failed hook object with a single saveScreenshot action.
+ * And a pause action when ENABLE_PAUSE is set to true.
*
* @param string $parentName
* @return TestHookObject
*/
public function createDefaultFailedHook($parentName)
{
-
- $saveScreenshotStep = [new ActionObject("saveScreenshot", "saveScreenshot", [])];
+ $defaultSteps['saveScreenshot'] = new ActionObject("saveScreenshot", "saveScreenshot", []);
+ if (getenv('ENABLE_PAUSE') === 'true') {
+ $defaultSteps['pauseWhenFailed'] = new ActionObject(
+ 'pauseWhenFailed',
+ 'pause',
+ [ActionObject::PAUSE_ACTION_INTERNAL_ATTRIBUTE => true]
+ );
+ }
$hook = new TestHookObject(
TestObjectExtractor::TEST_FAILED_HOOK,
$parentName,
- $saveScreenshotStep
+ $defaultSteps
);
return $hook;
diff --git a/src/Magento/FunctionalTestingFramework/Test/etc/Actions/customActions.xsd b/src/Magento/FunctionalTestingFramework/Test/etc/Actions/customActions.xsd
index 2aad30c75..b1d6e1b02 100644
--- a/src/Magento/FunctionalTestingFramework/Test/etc/Actions/customActions.xsd
+++ b/src/Magento/FunctionalTestingFramework/Test/etc/Actions/customActions.xsd
@@ -29,6 +29,12 @@
+
+
+
+
+
+
@@ -346,6 +352,26 @@
+
+
+
+ Used in an action group to return a value. Must be used only once in action group. Do not use in tests or suites.
+
+
+
+
+
+
+
+ Value or variable to be returned.
+
+
+
+
+
+
+
+
diff --git a/src/Magento/FunctionalTestingFramework/Test/etc/actionTypeTags.xsd b/src/Magento/FunctionalTestingFramework/Test/etc/actionTypeTags.xsd
index def2964af..d68cf43db 100644
--- a/src/Magento/FunctionalTestingFramework/Test/etc/actionTypeTags.xsd
+++ b/src/Magento/FunctionalTestingFramework/Test/etc/actionTypeTags.xsd
@@ -68,6 +68,12 @@
+
+
+
+
+
+
diff --git a/src/Magento/FunctionalTestingFramework/Test/etc/mergedActionGroupSchema.xsd b/src/Magento/FunctionalTestingFramework/Test/etc/mergedActionGroupSchema.xsd
index bd32ba879..f45c33acd 100644
--- a/src/Magento/FunctionalTestingFramework/Test/etc/mergedActionGroupSchema.xsd
+++ b/src/Magento/FunctionalTestingFramework/Test/etc/mergedActionGroupSchema.xsd
@@ -15,29 +15,21 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -57,4 +49,28 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Magento/FunctionalTestingFramework/Util/Logger/MftfLogger.php b/src/Magento/FunctionalTestingFramework/Util/Logger/MftfLogger.php
index 5955235ce..880dd9736 100644
--- a/src/Magento/FunctionalTestingFramework/Util/Logger/MftfLogger.php
+++ b/src/Magento/FunctionalTestingFramework/Util/Logger/MftfLogger.php
@@ -46,12 +46,14 @@ public function __construct($name, array $handlers = [], array $processors = [])
public function deprecation($message, array $context = [], $verbose = false)
{
$message = "DEPRECATION: " . $message;
- // print during test generation
- if ($this->phase === MftfApplicationConfig::GENERATION_PHASE && $verbose) {
+ // print during test generation including metadata
+ if ((array_key_exists('operationType', $context) ||
+ $this->phase === MftfApplicationConfig::GENERATION_PHASE) && $verbose) {
print ($message . json_encode($context) . "\n");
}
- // suppress logging during test execution
- if ($this->phase !== MftfApplicationConfig::EXECUTION_PHASE) {
+ // suppress logging during test execution except metadata
+ if (array_key_exists('operationType', $context) ||
+ $this->phase !== MftfApplicationConfig::EXECUTION_PHASE) {
parent::warning($message, $context);
}
}
diff --git a/src/Magento/FunctionalTestingFramework/Util/MftfGlobals.php b/src/Magento/FunctionalTestingFramework/Util/MftfGlobals.php
index d59df5c11..cb0f4f5ae 100644
--- a/src/Magento/FunctionalTestingFramework/Util/MftfGlobals.php
+++ b/src/Magento/FunctionalTestingFramework/Util/MftfGlobals.php
@@ -76,12 +76,15 @@ public static function getBackendBaseUrl($withTrailingSeparator = true)
{
if (!self::$backendBaseUrl) {
try {
+ $backendName = getenv('MAGENTO_BACKEND_NAME');
$bUrl = getenv('MAGENTO_BACKEND_BASE_URL');
- if ($bUrl) {
- self::$backendBaseUrl = UrlFormatter::format($bUrl, false);
+ if ($bUrl && $backendName) {
+ self::$backendBaseUrl = UrlFormatter::format(
+ UrlFormatter::format($bUrl) . $backendName,
+ false
+ );
} else {
$baseUrl = getenv('MAGENTO_BASE_URL');
- $backendName = getenv('MAGENTO_BACKEND_NAME');
if ($baseUrl && $backendName) {
self::$backendBaseUrl = UrlFormatter::format(
UrlFormatter::format($baseUrl) . $backendName,
diff --git a/src/Magento/FunctionalTestingFramework/Util/Script/ScriptUtil.php b/src/Magento/FunctionalTestingFramework/Util/Script/ScriptUtil.php
index 14f9c27e0..11afa5cc1 100644
--- a/src/Magento/FunctionalTestingFramework/Util/Script/ScriptUtil.php
+++ b/src/Magento/FunctionalTestingFramework/Util/Script/ScriptUtil.php
@@ -101,7 +101,7 @@ public function getModuleXmlFilesByScope($modulePaths, $scope)
if (!realpath($modulePath . $scopePath)) {
continue;
}
- $finder->files()->followLinks()->in($modulePath . $scopePath)->name("*.xml");
+ $finder->files()->followLinks()->in($modulePath . $scopePath)->name("*.xml")->sortByName();
$found = true;
}
return $found ? $finder->files() : [];
diff --git a/src/Magento/FunctionalTestingFramework/Util/TestGenerator.php b/src/Magento/FunctionalTestingFramework/Util/TestGenerator.php
index 8253a75ba..8caffc565 100644
--- a/src/Magento/FunctionalTestingFramework/Util/TestGenerator.php
+++ b/src/Magento/FunctionalTestingFramework/Util/TestGenerator.php
@@ -1172,6 +1172,16 @@ public function generateStepsPhp($actionObjects, $generationScope = TestGenerato
$selector
);
break;
+ case "return":
+ $actionOrigin = $actionObject->getActionOrigin();
+ $actionOriginStepKey = $actionOrigin[ActionGroupObject::ACTION_GROUP_ORIGIN_TEST_REF];
+ $testSteps .= $this->wrapFunctionCallWithReturnValue(
+ $actionOriginStepKey,
+ $actor,
+ $actionObject,
+ $value
+ );
+ break;
case "formatCurrency":
$testSteps .= $this->wrapFunctionCallWithReturnValue(
$stepKey,
@@ -1431,6 +1441,16 @@ public function generateStepsPhp($actionObjects, $generationScope = TestGenerato
$testSteps .= $dateGenerateCode;
break;
+ case "pause":
+ $pauseAttr = $actionObject->getCustomActionAttributes(
+ ActionObject::PAUSE_ACTION_INTERNAL_ATTRIBUTE
+ );
+ if ($pauseAttr) {
+ $testSteps .= sprintf("\t\t$%s->%s(%s);", $actor, $actionObject->getType(), 'true');
+ } else {
+ $testSteps .= sprintf("\t\t$%s->%s();", $actor, $actionObject->getType());
+ }
+ break;
case "comment":
$input = $input === null ? strtr($value, ['$' => '\$', '{' => '\{', '}' => '\}']) : $input;
// Combining userInput from native XML comment and action to fall-through 'default' case
@@ -2020,18 +2040,20 @@ private function resolveRuntimeReference($args, $regex, $func)
$newArgs = [];
foreach ($args as $key => $arg) {
+ $newArgs[$key] = $arg;
preg_match_all($regex, $arg, $matches);
if (!empty($matches[0])) {
- $fullMatch = $matches[0][0];
- $refVariable = $matches[1][0];
- unset($matches);
- $replacement = "{$func}(\"{$refVariable}\")";
+ foreach ($matches[0] as $matchKey => $fullMatch) {
+ $refVariable = $matches[1][$matchKey];
+
+ $replacement = $this->getReplacement($func, $refVariable);
- $outputArg = $this->processQuoteBreaks($fullMatch, $arg, $replacement);
- $newArgs[$key] = $outputArg;
+ $outputArg = $this->processQuoteBreaks($fullMatch, $newArgs[$key], $replacement);
+ $newArgs[$key] = $outputArg;
+ }
+ unset($matches);
continue;
}
- $newArgs[$key] = $arg;
}
// override passed in args for use later.
@@ -2303,4 +2325,20 @@ private function parseUserInput($userInput)
return $this->addUniquenessFunctionCall($userInput);
}
+
+ /**
+ * Supports fallback for BACKEND URL
+ *
+ * @param string $func
+ * @param string $refVariable
+ * @return string
+ */
+ private function getReplacement($func, $refVariable): string
+ {
+ if ($refVariable === 'MAGENTO_BACKEND_BASE_URL') {
+ return "({$func}(\"{$refVariable}\") ? rtrim({$func}(\"{$refVariable}\"), \"/\") : \"\")";
+ }
+
+ return "{$func}(\"{$refVariable}\")";
+ }
}