diff --git a/.travis.yml b/.travis.yml index fb21686b8..fba3b46ae 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,11 +2,16 @@ language: php php: - 7.2 - 7.3 +services: + - docker +before_install: + - docker run -d -p 4444:4444 -v /dev/shm:/dev/shm selenium/standalone-chrome:3.141.59-zirconium install: composer install --no-interaction --prefer-source env: matrix: - VERIFICATION_TOOL=phpunit-checks - VERIFICATION_TOOL=static-checks + - VERIFICATION_TOOL=functional script: - bin/$VERIFICATION_TOOL after_success: diff --git a/bin/functional b/bin/functional new file mode 100755 index 000000000..99377337e --- /dev/null +++ b/bin/functional @@ -0,0 +1,10 @@ +# Copyright © Magento, Inc. All rights reserved. +# See COPYING.txt for license details. + +set -e + +echo "===============================" +echo " EXECUTE DevDocsTest " +echo "===============================" +bin/mftf build:project +bin/mftf run:test DevDocsTest -f diff --git a/composer.json b/composer.json index 23b0848d5..74743f674 100755 --- a/composer.json +++ b/composer.json @@ -53,7 +53,7 @@ "files": ["src/Magento/FunctionalTestingFramework/_bootstrap.php"], "psr-4": { "Magento\\FunctionalTestingFramework\\": "src/Magento/FunctionalTestingFramework", - "MFTF\\": "dev/tests/functional/MFTF" + "MFTF\\": "dev/tests/functional/tests/MFTF" } }, "autoload-dev": { diff --git a/composer.lock b/composer.lock index 43be8f65d..8757833cb 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": "ddf9b7965d3a2369bf0f22f5172d75dc", + "content-hash": "797f6caab6c9a1ae60da15083d03a548", "packages": [ { "name": "allure-framework/allure-codeception", @@ -6872,7 +6872,8 @@ "php": "~7.2.0||~7.3.0", "ext-curl": "*", "ext-json": "*", - "ext-openssl": "*" + "ext-openssl": "*", + "ext-dom": "*" }, "platform-dev": [] } diff --git a/dev/tests/functional/tests/MFTF/DevDocs/ActionGroup/HelperActionGroup.xml b/dev/tests/functional/tests/MFTF/DevDocs/ActionGroup/HelperActionGroup.xml new file mode 100644 index 000000000..4e39a2da1 --- /dev/null +++ b/dev/tests/functional/tests/MFTF/DevDocs/ActionGroup/HelperActionGroup.xml @@ -0,0 +1,24 @@ + + + + + + + + + + {{contentSection.parametrizedSelector(test)}} + ['{{test}}', 'Bla'] + {{test}} + true + 4.400000000234234 + 42 + + + diff --git a/dev/tests/functional/tests/MFTF/DevDocs/Data/HelperData.xml b/dev/tests/functional/tests/MFTF/DevDocs/Data/HelperData.xml new file mode 100644 index 000000000..bc84a5fb2 --- /dev/null +++ b/dev/tests/functional/tests/MFTF/DevDocs/Data/HelperData.xml @@ -0,0 +1,14 @@ + + + + + + Some kind of data for testing purposes + + diff --git a/dev/tests/functional/tests/MFTF/DevDocs/Helper/CustomHelper.php b/dev/tests/functional/tests/MFTF/DevDocs/Helper/CustomHelper.php new file mode 100644 index 000000000..46fba18d9 --- /dev/null +++ b/dev/tests/functional/tests/MFTF/DevDocs/Helper/CustomHelper.php @@ -0,0 +1,49 @@ + 'value', 'test'] + ) { + print('Hello, this is custom helper which provides an ability to write custom solutions.' . PHP_EOL); + print('string $url = ' . $url . PHP_EOL); + print('$test = ' . $test . PHP_EOL); + print('$bool = ' . $bool . PHP_EOL); + print('$int = ' . $int . PHP_EOL); + print('$float = ' . $float . PHP_EOL); + print('array $module = [' . implode(', ', $module) . ']' . PHP_EOL); + print('$superBla = ' . $superBla . PHP_EOL); + print('$bla = ' . $bla . PHP_EOL); + print('array $arraysomething = [' . implode(', ', $arraysomething) . ']' . PHP_EOL); + } +} diff --git a/dev/tests/functional/tests/MFTF/DevDocs/Section/ContentSection.xml b/dev/tests/functional/tests/MFTF/DevDocs/Section/ContentSection.xml index c318fac3f..e0a133d51 100644 --- a/dev/tests/functional/tests/MFTF/DevDocs/Section/ContentSection.xml +++ b/dev/tests/functional/tests/MFTF/DevDocs/Section/ContentSection.xml @@ -10,6 +10,7 @@ xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd">
+
diff --git a/dev/tests/functional/tests/MFTF/DevDocs/Test/DeprecatedDevDocsTest.xml b/dev/tests/functional/tests/MFTF/DevDocs/Test/DeprecatedDevDocsTest.xml index 5944a964c..6aae104ed 100644 --- a/dev/tests/functional/tests/MFTF/DevDocs/Test/DeprecatedDevDocsTest.xml +++ b/dev/tests/functional/tests/MFTF/DevDocs/Test/DeprecatedDevDocsTest.xml @@ -22,6 +22,6 @@ - + diff --git a/dev/tests/functional/tests/MFTF/DevDocs/Test/DevDocsTest.xml b/dev/tests/functional/tests/MFTF/DevDocs/Test/DevDocsTest.xml index 592294889..e7c3bed54 100644 --- a/dev/tests/functional/tests/MFTF/DevDocs/Test/DevDocsTest.xml +++ b/dev/tests/functional/tests/MFTF/DevDocs/Test/DevDocsTest.xml @@ -22,5 +22,28 @@ + + {{contentSection.pageIntro}} + ['Test', 'Bla'] + {{MFTFDocPage.url}} + true + 1.2 + 123 + + + + {{contentSection.pageIntro}} + [] + {{DeprecatedMFTFDocPage.url}} + 1.2 + + false + 4.223 + 987 + + + + + diff --git a/etc/config/functional.suite.dist.yml b/etc/config/functional.suite.dist.yml index 10fe1212d..ffbb60990 100644 --- a/etc/config/functional.suite.dist.yml +++ b/etc/config/functional.suite.dist.yml @@ -12,8 +12,6 @@ namespace: Magento\FunctionalTestingFramework modules: enabled: - \Magento\FunctionalTestingFramework\Module\MagentoWebDriver - - \Magento\FunctionalTestingFramework\Helper\Acceptance - - \Magento\FunctionalTestingFramework\Helper\MagentoFakerData - \Magento\FunctionalTestingFramework\Module\MagentoSequence - \Magento\FunctionalTestingFramework\Module\MagentoAssert - \Magento\FunctionalTestingFramework\Module\MagentoActionProxies diff --git a/etc/di.xml b/etc/di.xml index 8d06383a5..1dd91896a 100644 --- a/etc/di.xml +++ b/etc/di.xml @@ -8,7 +8,7 @@ + ]> @@ -222,6 +222,8 @@ createDataKey key stepKey + name + name stepKey keyForRemoval keyForRemoval @@ -239,6 +241,8 @@ stepKey stepKey + name + name keyForRemoval keyForRemoval name @@ -299,6 +303,7 @@ name name + name stepKey createDataKey key @@ -316,6 +321,7 @@ keyForRemoval name name + name createDataKey key diff --git a/src/Magento/FunctionalTestingFramework/Helper/Acceptance.php b/src/Magento/FunctionalTestingFramework/Helper/Acceptance.php deleted file mode 100644 index fafe167af..000000000 --- a/src/Magento/FunctionalTestingFramework/Helper/Acceptance.php +++ /dev/null @@ -1,42 +0,0 @@ -getModule(MagentoWebDriver::class)->_reconfigure([$config => $value]); - } - - /** - * Get WebDriver configuration. - * - * @param string $config - * @return string - */ - public function getConfiguration($config) - { - return $this->getModule(MagentoWebDriver::class)->_getConfig($config); - } -} diff --git a/src/Magento/FunctionalTestingFramework/Helper/AdminUrlList.php b/src/Magento/FunctionalTestingFramework/Helper/AdminUrlList.php deleted file mode 100644 index ca96033dc..000000000 --- a/src/Magento/FunctionalTestingFramework/Helper/AdminUrlList.php +++ /dev/null @@ -1,189 +0,0 @@ -getMethod($method); + if ($method) { + $result = []; + /** @var $parameter \ReflectionParameter */ + foreach ($method->getParameters() as $parameter) { + try { + $result[$parameter->getName()] = [ + 'type' => $parameter->getType() === null ? null : $parameter->getType()->getName(), + 'variableName' => $parameter->getName(), + 'isOptional' => $parameter->isOptional(), + 'optionalValue' => $parameter->isOptional() ? + $parameter->isDefaultValueAvailable() ? $parameter->getDefaultValue() : null : + null + ]; + } catch (\ReflectionException $e) { + $message = $e->getMessage(); + throw new \ReflectionException($message, 0, $e); + } + } + } + + return $result; + } +} diff --git a/src/Magento/FunctionalTestingFramework/Helper/EntityRESTApiHelper.php b/src/Magento/FunctionalTestingFramework/Helper/EntityRESTApiHelper.php deleted file mode 100644 index 521ddc9ee..000000000 --- a/src/Magento/FunctionalTestingFramework/Helper/EntityRESTApiHelper.php +++ /dev/null @@ -1,112 +0,0 @@ - 'application/json']; - - /** - * Rest API client. - * - * @var Client - */ - private $guzzle_client; - - /** - * EntityRESTApiHelper constructor. - * @param string $host - * @param string $port - */ - public function __construct($host, $port) - { - $this->guzzle_client = new Client([ - 'base_uri' => "http://{$host}:{$port}", - 'timeout' => 5.0, - ]); - } - - /** - * Submit Auth API Request. - * - * @param string $apiMethod - * @param string $requestURI - * @param string $jsonBody - * @param array $headers - * @return \Psr\Http\Message\ResponseInterface - */ - public function submitAuthAPIRequest($apiMethod, $requestURI, $jsonBody, $headers) - { - $allHeaders = $headers; - $authTokenVal = $this->getAuthToken(); - $authToken = ['Authorization' => 'Bearer ' . $authTokenVal]; - $allHeaders = array_merge($allHeaders, $authToken); - - return $this->submitAPIRequest($apiMethod, $requestURI, $jsonBody, $allHeaders); - } - - /** - * Function that sends a REST call to the integration endpoint for an authorization token. - * - * @return string - */ - private function getAuthToken() - { - $jsonArray = json_encode(['username' => 'admin', 'password' => 'admin123']); - - $response = $this->submitAPIRequest( - 'POST', - self::INTEGRATION_ADMIN_TOKEN_URI, - $jsonArray, - self::APPLICATION_JSON_HEADER - ); - - if ($response->getStatusCode() != 200) { - throwException($response->getReasonPhrase() .' Could not get admin token from service, please check logs.'); - } - - $authToken = str_replace('"', "", $response->getBody()->getContents()); - return $authToken; - } - - /** - * Function that submits an api request from the guzzle client using the following parameters: - * - * @param string $apiMethod - * @param string $requestURI - * @param string $jsonBody - * @param array $headers - * @return \Psr\Http\Message\ResponseInterface - */ - private function submitAPIRequest($apiMethod, $requestURI, $jsonBody, $headers) - { - $response = $this->guzzle_client->request( - $apiMethod, - $requestURI, - [ - 'headers' => $headers, - 'body' => $jsonBody - ] - ); - - return $response; - } -} diff --git a/src/Magento/FunctionalTestingFramework/Helper/Helper.php b/src/Magento/FunctionalTestingFramework/Helper/Helper.php new file mode 100644 index 000000000..e47aca32b --- /dev/null +++ b/src/Magento/FunctionalTestingFramework/Helper/Helper.php @@ -0,0 +1,15 @@ +helpers = $helpers; + } + + /** + * Returns helper object by it's class name. + * + * @param string $className + * @return Helper + * @throws TestFrameworkException + */ + public function get(string $className) + { + if ($this->has($className)) { + return $this->helpers[$className]; + } + throw new TestFrameworkException('Custom helper ' . $className . 'not found.'); + } + + /** + * Verifies that helper object exist. + * + * @param string $className + * @return boolean + */ + public function has(string $className) + { + return array_key_exists($className, $this->helpers); + } +} diff --git a/src/Magento/FunctionalTestingFramework/Helper/MagentoFakerData.php b/src/Magento/FunctionalTestingFramework/Helper/MagentoFakerData.php deleted file mode 100644 index 9c28a3531..000000000 --- a/src/Magento/FunctionalTestingFramework/Helper/MagentoFakerData.php +++ /dev/null @@ -1,122 +0,0 @@ - $faker->title, - 'firstname' => $faker->firstName, - 'middlename' => $faker->firstName, - 'lastname' => $faker->lastName, - 'suffix' => \Faker\Provider\en_US\Person::suffix(), - 'email' => $faker->email, - 'dateOfBirth' => $faker->date('m/d/Y', 'now'), - 'gender' => rand(0, 1), - 'group_id' => 1, - 'store_id' => 1, - 'website_id' => 1, - 'taxVatNumber' => \Faker\Provider\at_AT\Payment::vat(), - 'company' => $faker->company, - 'phoneNumber' => $faker->phoneNumber, - 'address' => [ - 'address1' => $faker->streetAddress, - 'address2' => $faker->streetAddress, - 'city' => $faker->city, - 'country' => 'United States', - 'state' => \Faker\Provider\en_US\Address::state(), - 'zipCode' => $faker->postcode - ] - ]; - return array_merge($customerData, $additional); - } - - /** - * Get category data. - * - * @return array - */ - public function getCategoryData() - { - $faker = \Faker\Factory::create(); - - return [ - 'enableCategory' => $faker->boolean(), - 'includeInMenu' => $faker->boolean(), - 'categoryName' => $faker->md5, - 'categoryImage' => '', - 'description' => $faker->sentence(10, true), - 'addCMSBlock' => '', - - 'urlKey' => $faker->uuid, - 'metaTitle' => $faker->word, - 'metaKeywords' => $faker->sentence(5, true), - 'metaDescription' => $faker->sentence(10, true), - ]; - } - - /** - * Get simple product data. - * - * @return array - */ - public function getProductData() - { - $faker = \Faker\Factory::create(); - return [ - 'enableProduct' => $faker->boolean(), - 'attributeSet' => '', - 'productName' => $faker->text(20), - 'sku' => \Faker\Provider\DateTime::unixTime('now'), - 'price' => $faker->randomFloat(2, 0, 999), - 'quantity' => $faker->numberBetween(1, 999), - - 'urlKey' => $faker->uuid, - 'metaTitle' => $faker->word, - 'metaKeywords' => $faker->sentence(5, true), - 'metaDescription' => $faker->sentence(10, true) - ]; - } - - /** - * Get Content Page Data. - * - * @return array - */ - public function getContentPage() - { - $faker = \Faker\Factory::create(); - - $pageContent = [ - 'pageTitle' => $faker->sentence(3, true), - 'contentHeading' => $faker->sentence(3, true), - 'contentBody' => $faker->sentence(10, true), - 'urlKey' => $faker->uuid, - 'metaTitle' => $faker->word, - 'metaKeywords' => $faker->sentence(5, true), - 'metaDescription' => $faker->sentence(10, true), - 'from' => $faker->date($format = 'm/d/Y', 'now'), - 'to' => $faker->date($format = 'm/d/Y') - ]; - $pageContent['layoutUpdateXml'] = "ToveJaniReminder"; - $pageContent['layoutUpdateXml'] .= "Don't forget me this weekend!"; - - return $pageContent; - } -} diff --git a/src/Magento/FunctionalTestingFramework/Helper/views/TestInjectMethod.mustache b/src/Magento/FunctionalTestingFramework/Helper/views/TestInjectMethod.mustache new file mode 100644 index 000000000..d921120cf --- /dev/null +++ b/src/Magento/FunctionalTestingFramework/Helper/views/TestInjectMethod.mustache @@ -0,0 +1,19 @@ + /** + * @var \Magento\FunctionalTestingFramework\Helper\HelperContainer + */ + private $helperContainer; + + /** + * Special method which automatically creates the respective objects. + */ + public function _inject( + {{argumentsWithTypes}} + ) { + $this->helperContainer = new \Magento\FunctionalTestingFramework\Helper\HelperContainer( + [ + {{#arguments}} + '{{type}}' => {{var}}, + {{/arguments}} + ] + ); + } diff --git a/src/Magento/FunctionalTestingFramework/Test/Objects/ActionGroupObject.php b/src/Magento/FunctionalTestingFramework/Test/Objects/ActionGroupObject.php index dc1b158e1..3e1afae31 100644 --- a/src/Magento/FunctionalTestingFramework/Test/Objects/ActionGroupObject.php +++ b/src/Magento/FunctionalTestingFramework/Test/Objects/ActionGroupObject.php @@ -8,7 +8,6 @@ use Magento\FunctionalTestingFramework\Exceptions\TestReferenceException; use Magento\FunctionalTestingFramework\Test\Util\ActionMergeUtil; -use Magento\FunctionalTestingFramework\Test\Util\ObjectExtension; /** * Class ActionGroupObject diff --git a/src/Magento/FunctionalTestingFramework/Test/Objects/ActionObject.php b/src/Magento/FunctionalTestingFramework/Test/Objects/ActionObject.php index 0ae3d336e..648471c7a 100644 --- a/src/Magento/FunctionalTestingFramework/Test/Objects/ActionObject.php +++ b/src/Magento/FunctionalTestingFramework/Test/Objects/ActionObject.php @@ -75,6 +75,7 @@ class ActionObject const DEFAULT_COMMAND_WAIT_TIMEOUT = 60; const ACTION_ATTRIBUTE_USERINPUT = 'userInput'; const ACTION_TYPE_COMMENT = 'comment'; + const ACTION_TYPE_HELPER = 'helper'; const INVISIBLE_STEP_ACTIONS = ['retrieveEntityField', 'getSecret']; /** @@ -282,6 +283,7 @@ public function setTimeout($timeout) public function resolveReferences() { if (empty($this->resolvedCustomAttributes)) { + $this->resolveHelperReferences(); $this->trimAssertionAttributes(); $this->resolveSelectorReferenceAndTimeout(); $this->resolveUrlReference(); @@ -293,6 +295,64 @@ public function resolveReferences() } } + /** + * Resolves references for helpers. + * + * @throws TestReferenceException + * @return void + */ + private function resolveHelperReferences() + { + if ($this->getType() !== 'helper') { + return; + } + $isResolved = false; + + try { + foreach ($this->actionAttributes as $attrKey => $attrValue) { + $this->actionAttributes[$attrKey] = $this->findAndReplaceReferences( + SectionObjectHandler::getInstance(), + $attrValue + ); + } + $isResolved = true; + } catch (\Exception $e) { + // catching exception to allow other entity type resolution to proceed + } + + try { + foreach ($this->actionAttributes as $attrKey => $attrValue) { + $this->actionAttributes[$attrKey] = $this->findAndReplaceReferences( + PageObjectHandler::getInstance(), + $attrValue + ); + } + $isResolved = true; + } catch (\Exception $e) { + // catching exception to allow other entity type resolution to proceed + } + + try { + foreach ($this->actionAttributes as $attrKey => $attrValue) { + $this->actionAttributes[$attrKey] = $this->findAndReplaceReferences( + DataObjectHandler::getInstance(), + $attrValue + ); + } + $isResolved = true; + } catch (\Exception $e) { + // catching exception to allow other entity type resolution to proceed + } + + if ($isResolved !== true) { + throw new TestReferenceException( + "Could not resolve entity reference \"{$attrValue}\" " + . "in Action with stepKey \"{$this->getStepKey()}\"", + ["input" => $attrValue, "stepKey" => $this->getStepKey()] + ); + } + } + /** * Flattens expectedResult/actualResults/array nested elements, if necessary. * e.g. expectedResults[] -> ["expectedType" => "string", "expected" => "value"] diff --git a/src/Magento/FunctionalTestingFramework/Test/Util/ActionObjectExtractor.php b/src/Magento/FunctionalTestingFramework/Test/Util/ActionObjectExtractor.php index fc874fa24..d9b8d32fb 100644 --- a/src/Magento/FunctionalTestingFramework/Test/Util/ActionObjectExtractor.php +++ b/src/Magento/FunctionalTestingFramework/Test/Util/ActionObjectExtractor.php @@ -22,6 +22,7 @@ class ActionObjectExtractor extends BaseObjectExtractor const TEST_ACTION_AFTER = 'after'; const TEST_STEP_MERGE_KEY = 'stepKey'; const ACTION_GROUP_TAG = 'actionGroup'; + const HELPER_TAG = 'helper'; const ACTION_GROUP_REF = 'ref'; const ACTION_GROUP_ARGUMENTS = 'arguments'; const ACTION_GROUP_ARG_VALUE = 'value'; @@ -84,6 +85,7 @@ public function extractActions($testActions, $testName = null) } $actionAttributes = $this->processActionGroupArgs($actionType, $actionAttributes); + $actionAttributes = $this->processHelperArgs($actionType, $actionAttributes); $linkedAction = $this->processLinkedActions($actionName, $actionData); $actions = $this->extractFieldActions($actionData, $actions); $actionAttributes = $this->extractFieldReferences($actionData, $actionAttributes); @@ -167,6 +169,42 @@ private function processActionGroupArgs($actionType, $actionAttributeData) return $actionAttributeArgData; } + /** + * Takes the helper arguments as an array that can be passed to PHP class + * defined in the action group xml. + * + * @param string $actionType + * @param array $actionAttributeData + * @return array + * @throws TestFrameworkException + */ + private function processHelperArgs($actionType, $actionAttributeData) + { + $reservedHelperVariableNames = ['class', 'method']; + if ($actionType !== self::HELPER_TAG) { + return $actionAttributeData; + } + + $actionAttributeArgData = []; + foreach ($actionAttributeData as $attributeDataKey => $attributeDataValues) { + if (isset($attributeDataValues['nodeName']) && $attributeDataValues['nodeName'] == 'argument') { + if (isset($attributeDataValues['name']) + && in_array($attributeDataValues['name'], $reservedHelperVariableNames)) { + $message = 'Helper argument names ' . implode(',', $reservedHelperVariableNames); + $message .= ' are reserved and can not be used.'; + throw new TestFrameworkException( + $message + ); + } + $actionAttributeArgData[$attributeDataValues['name']] = $attributeDataValues['value'] ?? null; + continue; + } + $actionAttributeArgData[$attributeDataKey] = $attributeDataValues; + } + + return $actionAttributeArgData; + } + /** * Takes the array representing an action and validates it is a persistence type. If of type persistence, * the function checks for any user specified fields to extract as separate actions to be resolved independently diff --git a/src/Magento/FunctionalTestingFramework/Test/etc/Actions/customActions.xsd b/src/Magento/FunctionalTestingFramework/Test/etc/Actions/customActions.xsd index 8670d9885..fbb21456f 100644 --- a/src/Magento/FunctionalTestingFramework/Test/etc/Actions/customActions.xsd +++ b/src/Magento/FunctionalTestingFramework/Test/etc/Actions/customActions.xsd @@ -11,6 +11,7 @@ + @@ -29,6 +30,25 @@ + + + + + + + + + + + + + + + + + + + diff --git a/src/Magento/FunctionalTestingFramework/Util/TestGenerator.php b/src/Magento/FunctionalTestingFramework/Util/TestGenerator.php index e0543463d..19319d0d7 100644 --- a/src/Magento/FunctionalTestingFramework/Util/TestGenerator.php +++ b/src/Magento/FunctionalTestingFramework/Util/TestGenerator.php @@ -25,6 +25,8 @@ use Magento\FunctionalTestingFramework\Util\Filesystem\DirSetupUtil; use Magento\FunctionalTestingFramework\Test\Util\ActionMergeUtil; use Magento\FunctionalTestingFramework\Util\Path\FilePathFormatter; +use Mustache_Engine; +use Mustache_Loader_FilesystemLoader; /** * Class TestGenerator @@ -60,6 +62,13 @@ class TestGenerator const ARRAY_WRAP_OPEN = '['; const ARRAY_WRAP_CLOSE = ']'; + /** + * Array with helpers classes and methods. + * + * @var array + */ + private $customHelpers = []; + /** * Actor name for AcceptanceTest * @@ -268,6 +277,7 @@ public function assembleTestPhp($testObject) $cestPhp .= $classAnnotationsPhp; $cestPhp .= sprintf("class %s\n", $className); $cestPhp .= "{\n"; + $cestPhp .= $this->generateInjectMethod(); $cestPhp .= $hookPhp; $cestPhp .= $testsPhp; $cestPhp .= "}\n"; @@ -275,6 +285,35 @@ public function assembleTestPhp($testObject) return $cestPhp; } + /** + * Generates _injectMethod based on $this->customHelpers. + * + * @return string + */ + private function generateInjectMethod() + { + if (empty($this->customHelpers)) { + return ""; + } + + $mustacheEngine = new Mustache_Engine([ + 'loader' => new Mustache_Loader_FilesystemLoader( + dirname(__DIR__) . DIRECTORY_SEPARATOR . "Helper" . DIRECTORY_SEPARATOR . 'views' + ) + ]); + + $argumentsWithType = []; + $arguments = []; + foreach ($this->customHelpers as $customHelperVar => $customHelperType) { + $argumentsWithType[] = $customHelperType . ' ' . $customHelperVar; + $arguments[] = ['type' => $customHelperType, 'var' => $customHelperVar]; + } + $mustacheData['argumentsWithTypes'] = implode(', ' . PHP_EOL, $argumentsWithType); + $mustacheData['arguments'] = $arguments; + + return $mustacheEngine->render('TestInjectMethod', $mustacheData); + } + /** * Load ALL Test objects. Loop over and pass each to the assembleTestPhp function. * @@ -774,6 +813,45 @@ public function generateStepsPhp($actionObjects, $generationScope = TestGenerato } switch ($actionObject->getType()) { + case "helper": + if (!in_array($customActionAttributes['class'], $this->customHelpers)) { + $this->customHelpers['$' . $stepKey] = $customActionAttributes['class']; + } + + $arguments = []; + $classReader = new \Magento\FunctionalTestingFramework\Helper\Code\ClassReader(); + $parameters = $classReader->getParameters( + $customActionAttributes['class'], + $customActionAttributes['method'] + ); + $errors = []; + foreach ($parameters as $parameter) { + if (array_key_exists($parameter['variableName'], $customActionAttributes)) { + $value = $customActionAttributes[$parameter['variableName']]; + $arguments[] = $this->addUniquenessFunctionCall( + $value, + $parameter['type'] === 'string' || $parameter['type'] === null + ); + } elseif ($parameter['isOptional']) { + $value = $parameter['optionalValue']; + $arguments[] = str_replace(PHP_EOL, '', var_export($value, true)); + } else { + $errors[] = 'Argument \'' . $parameter['variableName'] . '\' for method ' + . $customActionAttributes['class'] . '::' . $customActionAttributes['method'] + . ' is not found.'; + } + } + if (!empty($errors)) { + throw new TestFrameworkException(implode(PHP_EOL, $errors)); + } + $testSteps .= sprintf( + "\t\t$%s->comment('[%s] %s()');" . PHP_EOL, + $actor, + $stepKey, + $customActionAttributes['class'] . '::' . $customActionAttributes['method'] + ); + $testSteps .= $this->wrapFunctionCall($actor, $actionObject, $arguments); + break; case "createData": $entity = $customActionAttributes['entity']; @@ -1841,7 +1919,15 @@ private function addDollarSign($input) private function wrapFunctionCall($actor, $action, ...$args) { $isFirst = true; - $output = sprintf("\t\t$%s->%s(", $actor, $action->getType()); + $isActionHelper = $action->getType() === 'helper'; + $actionType = $action->getType(); + if ($isActionHelper) { + $actor = "this->helperContainer->get('" . $action->getCustomActionAttributes()['class'] . "')"; + $args = $args[0]; + $actionType = $action->getCustomActionAttributes()['method']; + } + + $output = sprintf("\t\t$%s->%s(", $actor, $actionType); for ($i = 0; $i < count($args); $i++) { if (null === $args[$i]) { continue;