From af6346a7c5a714787e18ee0001e71a098dbf3a74 Mon Sep 17 00:00:00 2001 From: Kevin Kozan Date: Wed, 13 Sep 2017 14:21:31 -0500 Subject: [PATCH 01/11] MQE-355 Added line back into actionObject. --- .../FunctionalTestingFramework/Test/Objects/ActionObject.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Magento/FunctionalTestingFramework/Test/Objects/ActionObject.php b/src/Magento/FunctionalTestingFramework/Test/Objects/ActionObject.php index de3e58e5c..40bd1d17d 100644 --- a/src/Magento/FunctionalTestingFramework/Test/Objects/ActionObject.php +++ b/src/Magento/FunctionalTestingFramework/Test/Objects/ActionObject.php @@ -323,6 +323,7 @@ private function findAndReplaceReferences($objectHandler, $inputString) list(,$objField) = $this->stripAndSplitReference($match); $parameterized = $obj->getElement($objField)->isParameterized(); $replacement = $obj->getElement($objField)->getLocator(); + $this->timeout = $obj->getElement($objField)->getTimeout(); break; case (get_class($obj) == EntityDataObject::class): list(,$objField) = $this->stripAndSplitReference($match); From 1f81574c66345a146ec84565aa2a403bc5f71226 Mon Sep 17 00:00:00 2001 From: Kevin Kozan Date: Wed, 13 Sep 2017 14:22:59 -0500 Subject: [PATCH 02/11] MQE-359 Removed addslashes, added replace of " to \" since " is read in a " via the xml parser. Rename wrapWithSingleQuotes to wrapWithDoubleQuotes, and fixed PHP Doc for the function. --- .../Util/TestGenerator.php | 35 ++++++++++--------- 1 file changed, 18 insertions(+), 17 deletions(-) diff --git a/src/Magento/FunctionalTestingFramework/Util/TestGenerator.php b/src/Magento/FunctionalTestingFramework/Util/TestGenerator.php index 6799e4bb5..804468aa1 100644 --- a/src/Magento/FunctionalTestingFramework/Util/TestGenerator.php +++ b/src/Magento/FunctionalTestingFramework/Util/TestGenerator.php @@ -336,15 +336,15 @@ private function generateStepsPhp($stepsObject, $stepsData, $hookObject = false) if (isset($customActionAttributes['selectorArray'])) { $selector = $customActionAttributes['selectorArray']; } elseif (isset($customActionAttributes['selector'])) { - $selector = $this->wrapWithSingleQuotes($customActionAttributes['selector']); + $selector = $this->wrapWithDoubleQuotes($customActionAttributes['selector']); } if (isset($customActionAttributes['selector1'])) { - $selector1 = $this->wrapWithSingleQuotes($customActionAttributes['selector1']); + $selector1 = $this->wrapWithDoubleQuotes($customActionAttributes['selector1']); } if (isset($customActionAttributes['selector2'])) { - $selector2 = $this->wrapWithSingleQuotes($customActionAttributes['selector2']); + $selector2 = $this->wrapWithDoubleQuotes($customActionAttributes['selector2']); } if (isset($customActionAttributes['x'])) { @@ -364,15 +364,15 @@ private function generateStepsPhp($stepsObject, $stepsData, $hookObject = false) } if (isset($customActionAttributes['locale'])) { - $locale = $this->wrapWithSingleQuotes($customActionAttributes['locale']); + $locale = $this->wrapWithDoubleQuotes($customActionAttributes['locale']); } if (isset($customActionAttributes['username'])) { - $username = $this->wrapWithSingleQuotes($customActionAttributes['username']); + $username = $this->wrapWithDoubleQuotes($customActionAttributes['username']); } if (isset($customActionAttributes['password'])) { - $password = $this->wrapWithSingleQuotes($customActionAttributes['password']); + $password = $this->wrapWithDoubleQuotes($customActionAttributes['password']); } if (isset($customActionAttributes['width'])) { @@ -384,15 +384,15 @@ private function generateStepsPhp($stepsObject, $stepsData, $hookObject = false) } if (isset($customActionAttributes['value'])) { - $value = $this->wrapWithSingleQuotes($customActionAttributes['value']); + $value = $this->wrapWithDoubleQuotes($customActionAttributes['value']); } if (isset($customActionAttributes['button'])) { - $button = $this->wrapWithSingleQuotes($customActionAttributes['button']); + $button = $this->wrapWithDoubleQuotes($customActionAttributes['button']); } if (isset($customActionAttributes['parameter'])) { - $parameter = $this->wrapWithSingleQuotes($customActionAttributes['parameter']); + $parameter = $this->wrapWithDoubleQuotes($customActionAttributes['parameter']); } switch ($actionName) { @@ -583,7 +583,7 @@ private function generateStepsPhp($stepsObject, $stepsData, $hookObject = false) $testSteps .= $this->wrapFunctionCall($actor, $actionName, $function); break; case "executeJS": - $testSteps .= $this->wrapFunctionCall($actor, $actionName, $this->wrapWithSingleQuotes($function)); + $testSteps .= $this->wrapFunctionCall($actor, $actionName, $this->wrapWithDoubleQuotes($function)); break; case "performOn": case "waitForElementChange": @@ -593,7 +593,7 @@ private function generateStepsPhp($stepsObject, $stepsData, $hookObject = false) $testSteps .= $this->wrapFunctionCall( $actor, $actionName, - $this->wrapWithSingleQuotes($function), + $this->wrapWithDoubleQuotes($function), $time ); break; @@ -971,31 +971,32 @@ private function addUniquenessFunctionCall($input) $parts[$i] = $this->stripWrappedQuotes($parts[$i]); } if (!empty($parts[0])) { - $output = $this->wrapWithSingleQuotes($parts[0]); + $output = $this->wrapWithDoubleQuotes($parts[0]); } $output .= $output === '' ? $matches[0] : '.' . $matches[0]; if (!empty($parts[1])) { - $output .= '.' . $this->wrapWithSingleQuotes($parts[1]); + $output .= '.' . $this->wrapWithDoubleQuotes($parts[1]); } } else { - $output = $this->wrapWithSingleQuotes($input); + $output = $this->wrapWithDoubleQuotes($input); } return $output; } /** - * Wrap input string with single quotes. + * Wrap input string with double quotes, and replaces " with \" to prevent broken PHP when generated. * * @param string $input * @return string */ - private function wrapWithSingleQuotes($input) + private function wrapWithDoubleQuotes($input) { if (empty($input)) { return ''; } - $input = addslashes($input); + //Only replace " with \" so that it doesn't break outer string. + $input = str_replace('"', '\"', $input); return sprintf('"%s"', $input); } From a0f8da5fabcfcbcdd188077caa7662af55e1ae01 Mon Sep 17 00:00:00 2001 From: John Stennett Date: Thu, 14 Sep 2017 08:51:40 -0500 Subject: [PATCH 03/11] - Adding additional checks for all 3 spinners in the Magento grid UI components. - Removing the $id property from the uniqid function so it doesn't include it in the random number that is generated. This breaks many fields in the admin sense they don't accept special characters. --- .../FunctionalTestingFramework/Module/MagentoWebDriver.php | 3 +++ src/Magento/FunctionalTestingFramework/Util/msq.php | 4 ++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/Magento/FunctionalTestingFramework/Module/MagentoWebDriver.php b/src/Magento/FunctionalTestingFramework/Module/MagentoWebDriver.php index 02803846b..660055d18 100644 --- a/src/Magento/FunctionalTestingFramework/Module/MagentoWebDriver.php +++ b/src/Magento/FunctionalTestingFramework/Module/MagentoWebDriver.php @@ -195,6 +195,9 @@ public function waitForPageLoad($timeout = 15) { $this->waitForJS('return document.readyState == "complete"', $timeout); $this->waitForAjaxLoad($timeout); + $this->waitForElementNotVisible('.loading-mask', 30); + $this->waitForElementNotVisible('.admin_data-grid-loading-mask', 30); + $this->waitForElementNotVisible('.admin__form-loading-mask', 30); } /** diff --git a/src/Magento/FunctionalTestingFramework/Util/msq.php b/src/Magento/FunctionalTestingFramework/Util/msq.php index 4ab212580..621de31ac 100644 --- a/src/Magento/FunctionalTestingFramework/Util/msq.php +++ b/src/Magento/FunctionalTestingFramework/Util/msq.php @@ -14,7 +14,7 @@ function msq($id = null) return MagentoSequence::$hash[$id]; } $prefix = MagentoSequence::$prefix; - $sequence = $prefix . uniqid($id); + $sequence = $prefix . uniqid(); if ($id) { MagentoSequence::$hash[$id] = $sequence; } @@ -35,7 +35,7 @@ function msqs($id = null) return MagentoSequence::$suiteHash[$id]; } $prefix = MagentoSequence::$prefix; - $sequence = $prefix . uniqid($id); + $sequence = $prefix . uniqid(); if ($id) { MagentoSequence::$suiteHash[$id] = $sequence; } From db4cd0d2ab802aca104f08e1f20b670233263f83 Mon Sep 17 00:00:00 2001 From: Ian Meron Date: Thu, 14 Sep 2017 09:30:08 -0500 Subject: [PATCH 04/11] MQE-351:Single test run for running tests in parallel mode - add Test Manifest Class - invoke new class during TestGenerator runs - change hardcoded slash to PHP constant - add directory clear between generate calls --- .../Util/TestGenerator.php | 68 ++++++++++++++++--- .../Util/TestManifest.php | 52 ++++++++++++++ 2 files changed, 111 insertions(+), 9 deletions(-) create mode 100644 src/Magento/FunctionalTestingFramework/Util/TestManifest.php diff --git a/src/Magento/FunctionalTestingFramework/Util/TestGenerator.php b/src/Magento/FunctionalTestingFramework/Util/TestGenerator.php index 804468aa1..c9226abb0 100644 --- a/src/Magento/FunctionalTestingFramework/Util/TestGenerator.php +++ b/src/Magento/FunctionalTestingFramework/Util/TestGenerator.php @@ -6,13 +6,24 @@ namespace Magento\FunctionalTestingFramework\Util; +use FilesystemIterator; use Magento\FunctionalTestingFramework\DataGenerator\Objects\EntityDataObject; use Magento\FunctionalTestingFramework\Test\Handlers\CestObjectHandler; use Magento\FunctionalTestingFramework\Test\Objects\ActionObject; use Magento\FunctionalTestingFramework\DataGenerator\Handlers\DataObjectHandler; +use Magento\FunctionalTestingFramework\Test\Objects\CestObject; +use RecursiveDirectoryIterator; class TestGenerator { + + /** + * Path to the export dir. + * + * @var string + */ + private $exportDirectory; + /** * Test generator. * @@ -23,9 +34,47 @@ class TestGenerator /** * TestGenerator constructor. */ - private function __construct() + private function __construct($exportDir) { // private constructor for singleton + $this->exportDirectory = $exportDir; + } + + /** + * Method used to clean export dir if needed and create new empty export dir. + * + * @return void + */ + private function setupExportDir() + { + if (file_exists($this->exportDirectory)) { + $this->rmDirRecursive($this->exportDirectory); + } + + mkdir($this->exportDirectory, 0777, true); + } + + /** + * Takes a directory path and recursively deletes all files and folders. + * + * @param string $directory + */ + private function rmdirRecursive($directory) + { + $it = new RecursiveDirectoryIterator($directory, FilesystemIterator::SKIP_DOTS); + + while($it->valid()) { + $path = $directory . DIRECTORY_SEPARATOR . $it->getFilename(); + if ($it->isDir()) { + $this->rmDirRecursive($path); + } else { + unlink($path); + } + + $it->next(); + } + + rmdir($directory); } /** @@ -36,7 +85,7 @@ private function __construct() public static function getInstance() { if (!self::$testGenerator) { - self::$testGenerator = new TestGenerator(); + self::$testGenerator = new TestGenerator(TESTS_MODULE_PATH . DIRECTORY_SEPARATOR . "_generated"); } return self::$testGenerator; @@ -63,13 +112,7 @@ private function loadAllCestObjects() */ private function createCestFile($cestPhp, $filename) { - $exportDirectory = TESTS_MODULE_PATH . "/_generated"; - $exportFilePath = sprintf("%s/%s.php", $exportDirectory, $filename); - - if (!is_dir($exportDirectory)) { - mkdir($exportDirectory, 0777, true); - } - + $exportFilePath = $this->exportDirectory . DIRECTORY_SEPARATOR . $filename . ".php"; $file = fopen($exportFilePath, 'w'); if (!$file) { @@ -88,6 +131,7 @@ private function createCestFile($cestPhp, $filename) */ public function createAllCestFiles() { + $this->setupExportDir(); $cestPhpArray = $this->assembleAllCestPhp(); foreach ($cestPhpArray as $cestPhpFile) { @@ -134,11 +178,17 @@ private function assembleAllCestPhp() $cestObjects = $this->loadAllCestObjects(); $cestPhpArray = []; + // create our manifest file here + $testManifest = new TestManifest($this->exportDirectory); + foreach ($cestObjects as $cest) { $name = $cest->getName(); $name = $string = str_replace(' ', '', $name); $php = $this->assembleCestPhp($cest); $cestPhpArray[] = [$name, $php]; + + //write to manifest here + $testManifest->recordCest($cest->getName(), $cest->getTests()); } return $cestPhpArray; diff --git a/src/Magento/FunctionalTestingFramework/Util/TestManifest.php b/src/Magento/FunctionalTestingFramework/Util/TestManifest.php new file mode 100644 index 000000000..9202f62ef --- /dev/null +++ b/src/Magento/FunctionalTestingFramework/Util/TestManifest.php @@ -0,0 +1,52 @@ +filePath = $filePath; + $fileResource = fopen($filePath, 'w'); + fclose($fileResource); + } + + /** + * Takes a cest name and set of tests, records the names in a file for codeception to consume. + * + * @param string $cestName + * @param TestObject $tests + * @return void + */ + public function recordCest($cestName, $tests) + { + $fileResource = fopen($this->filePath, 'a'); + + foreach ($tests as $test) + { + $line = $cestName . ':' . $test->getName(); + fwrite($fileResource, $line ."\n"); + } + + fclose($fileResource); + } +} \ No newline at end of file From 4f21a7437e8be051c4e3f22697fbad67738a841c Mon Sep 17 00:00:00 2001 From: Ian Meron Date: Thu, 14 Sep 2017 10:34:30 -0500 Subject: [PATCH 05/11] MQE-351:Single test run for running tests in parallel mode - add relative filepath and php ending to testManifest output --- .../FunctionalTestingFramework/Util/TestManifest.php | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/Magento/FunctionalTestingFramework/Util/TestManifest.php b/src/Magento/FunctionalTestingFramework/Util/TestManifest.php index 9202f62ef..c8d635881 100644 --- a/src/Magento/FunctionalTestingFramework/Util/TestManifest.php +++ b/src/Magento/FunctionalTestingFramework/Util/TestManifest.php @@ -17,6 +17,13 @@ class TestManifest */ private $filePath; + /** + * Relative dir path from functional yml file. For devOps execution flexibility. + * + * @var string + */ + private $relativeDirPath; + /** * TestManifest constructor. * @@ -24,6 +31,7 @@ class TestManifest */ public function __construct($path) { + $this->relativeDirPath = substr($path, strlen(dirname(dirname(TESTS_BP))) + 1); $filePath = $path . DIRECTORY_SEPARATOR . 'testManifest.txt'; $this->filePath = $filePath; $fileResource = fopen($filePath, 'w'); @@ -43,7 +51,7 @@ public function recordCest($cestName, $tests) foreach ($tests as $test) { - $line = $cestName . ':' . $test->getName(); + $line = $this->relativeDirPath . DIRECTORY_SEPARATOR . $cestName . '.php:' . $test->getName(); fwrite($fileResource, $line ."\n"); } From 65a0f1e95e6d1c77024f8bce6d17320be5d560f5 Mon Sep 17 00:00:00 2001 From: Kevin Kozan Date: Tue, 19 Sep 2017 09:28:12 -0500 Subject: [PATCH 06/11] MQE-334 --- .../DataGenerator/Api/ApiExecutor.php | 49 +++++++++++++++---- .../Handlers/JsonDefinitionObjectHandler.php | 5 +- .../DataGenerator/Objects/JsonElement.php | 24 ++++++++- .../Util/JsonObjectExtractor.php | 5 +- .../DataGenerator/etc/dataOperation.xsd | 3 ++ 5 files changed, 73 insertions(+), 13 deletions(-) diff --git a/src/Magento/FunctionalTestingFramework/DataGenerator/Api/ApiExecutor.php b/src/Magento/FunctionalTestingFramework/DataGenerator/Api/ApiExecutor.php index ee0f6eb81..afd9fa351 100644 --- a/src/Magento/FunctionalTestingFramework/DataGenerator/Api/ApiExecutor.php +++ b/src/Magento/FunctionalTestingFramework/DataGenerator/Api/ApiExecutor.php @@ -3,6 +3,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\FunctionalTestingFramework\DataGenerator\Api; use Magento\FunctionalTestingFramework\DataGenerator\Handlers\DataObjectHandler; @@ -12,6 +13,7 @@ use Magento\FunctionalTestingFramework\DataGenerator\Objects\JsonElement; use Magento\FunctionalTestingFramework\DataGenerator\Util\JsonObjectExtractor; use Magento\FunctionalTestingFramework\Util\ApiClientUtil; +use Magento\Setup\Exception; /** * Class ApiExecutor @@ -19,6 +21,7 @@ class ApiExecutor { const PRIMITIVE_TYPES = ['string', 'boolean', 'integer', 'double', 'array']; + const EXCEPTION_REQUIRED_DATA = "%s of key \" %s\" in \"%s\" is required by metadata, but was not provided."; /** * Describes the operation for the executor ('create','update','delete') @@ -149,6 +152,7 @@ private function getAuthorizationHeader($authUrl) * @param EntityDataObject $entityObject * @param array $jsonArrayMetadata * @return array + * @throws \Exception */ private function convertJsonArray($entityObject, $jsonArrayMetadata) { @@ -169,20 +173,40 @@ private function convertJsonArray($entityObject, $jsonArrayMetadata) EntityDataObject::CEST_UNIQUE_VALUE ); - if (array_key_exists($jsonElement->getKey(), $entityObject->getUniquenessData())) { - $uniqueData = $entityObject->getUniquenessDataByName($jsonElement->getKey()); - if ($uniqueData === 'suffix') { - $elementData .= (string)self::getSequence($entityObject->getName()); - } else { - $elementData = (string)self::getSequence($entityObject->getName()) - . $elementData; + // If data was defined at all, attempt to put it into JSON body + // If data was not defined, and element is required, throw exception + // If no data is defined, don't input defaults per primitive into JSON for the data + if ($elementData != null) { + if (array_key_exists($jsonElement->getKey(), $entityObject->getUniquenessData())) { + $uniqueData = $entityObject->getUniquenessDataByName($jsonElement->getKey()); + if ($uniqueData === 'suffix') { + $elementData .= (string)self::getSequence($entityObject->getName()); + } else { + $elementData = (string)self::getSequence($entityObject->getName()) . $elementData; + } } + $jsonArray[$jsonElement->getKey()] = $this->castValue($jsonElementType, $elementData); + + } elseif ($jsonElement->getRequired()) { + throw new \Exception(sprintf( + ApiExecutor::EXCEPTION_REQUIRED_DATA, + $jsonElement->getType(), + $jsonElement->getKey(), + $this->entityObject->getName() + )); } - - $jsonArray[$jsonElement->getKey()] = $this->castValue($jsonElementType, $elementData); } else { $entityNamesOfType = $entityObject->getLinkedEntitiesOfType($jsonElementType); + // If an element is required by metadata, but was not provided in the entity, throw an exception + if ($jsonElement->getRequired() && $entityNamesOfType == null) { + throw new \Exception(sprintf( + ApiExecutor::EXCEPTION_REQUIRED_DATA, + $jsonElement->getType(), + $jsonElement->getKey(), + $this->entityObject->getName() + )); + } foreach ($entityNamesOfType as $entityName) { $jsonDataSubArray = $this->resolveNonPrimitiveElement($entityName, $jsonElement); @@ -210,7 +234,8 @@ private function resolveNonPrimitiveElement($entityName, $jsonElement) $linkedEntityObj = $this->resolveLinkedEntityObject($entityName); if (!empty($jsonElement->getNestedJsonElement($jsonElement->getValue())) - && $jsonElement->getType() == 'array') { + && $jsonElement->getType() == 'array' + ) { $jsonSubArray = $this->convertJsonArray( $linkedEntityObj, [$jsonElement->getNestedJsonElement($jsonElement->getValue())] @@ -285,6 +310,7 @@ private static function getSequence($entityName) } // @codingStandardsIgnoreStart + /** * This function takes a string value and its corresponding type and returns the string cast * into its the type passed. @@ -304,6 +330,9 @@ private function castValue($type, $value) $newVal = (integer)$value; break; case 'boolean': + if (strtolower($newVal) === 'false') { + return false; + } $newVal = (boolean)$value; break; case 'double': diff --git a/src/Magento/FunctionalTestingFramework/DataGenerator/Handlers/JsonDefinitionObjectHandler.php b/src/Magento/FunctionalTestingFramework/DataGenerator/Handlers/JsonDefinitionObjectHandler.php index 165962409..9243ff46e 100644 --- a/src/Magento/FunctionalTestingFramework/DataGenerator/Handlers/JsonDefinitionObjectHandler.php +++ b/src/Magento/FunctionalTestingFramework/DataGenerator/Handlers/JsonDefinitionObjectHandler.php @@ -37,6 +37,7 @@ class JsonDefinitionObjectHandler implements ObjectHandlerInterface const ENTITY_OPERATION_OBJECT_KEY = 'key'; const ENTITY_OPERATION_OBJECT_VALUE = 'value'; const ENTITY_OPERATION_JSON_OBJECT = 'jsonObject'; + const ENTITY_OPERATION_REQUIRED = 'required'; /** * Singleton Instance of class @@ -164,7 +165,8 @@ private function initJsonDefinitions() $jsonMetadata[] = new JsonElement( $jsonEntryType[JsonDefinitionObjectHandler::ENTITY_OPERATION_ENTRY_KEY], $jsonEntryType[JsonDefinitionObjectHandler::ENTITY_OPERATION_ENTRY_VALUE], - JsonDefinitionObjectHandler::ENTITY_OPERATION_ENTRY + JsonDefinitionObjectHandler::ENTITY_OPERATION_ENTRY, + $jsonEntryType[JsonDefinitionObjectHandler::ENTITY_OPERATION_REQUIRED] ?? null ); } } @@ -192,6 +194,7 @@ private function initJsonDefinitions() $jsonEntryType[JsonDefinitionObjectHandler::ENTITY_OPERATION_ARRAY_KEY], $value, $type, + $jsonEntryType[JsonDefinitionObjectHandler::ENTITY_OPERATION_REQUIRED] ?? null, $jsonSubMetadata ); } diff --git a/src/Magento/FunctionalTestingFramework/DataGenerator/Objects/JsonElement.php b/src/Magento/FunctionalTestingFramework/DataGenerator/Objects/JsonElement.php index 03f0192cc..975d44f06 100644 --- a/src/Magento/FunctionalTestingFramework/DataGenerator/Objects/JsonElement.php +++ b/src/Magento/FunctionalTestingFramework/DataGenerator/Objects/JsonElement.php @@ -43,20 +43,32 @@ class JsonElement */ private $nestedMetadata = []; + /** + * Json required attribute, used to determine if values need to be cast before insertion. + * @var bool + */ + private $required; + /** * JsonElement constructor. * @param string $key * @param string $value * @param string $type * @param array $nestedElements + * @param bool $required * @param array $nestedMetadata */ - public function __construct($key, $value, $type, $nestedElements = [], $nestedMetadata = null) + public function __construct($key, $value, $type, $required, $nestedElements = [], $nestedMetadata = null) { $this->key = $key; $this->value = $value; $this->type = $type; $this->nestedElements = $nestedElements; + if ($required) { + $this->required = true; + } else { + $this->required = false; + } $this->nestedMetadata = $nestedMetadata; } @@ -90,6 +102,16 @@ public function getType() return $this->type; } + /** + * Getter for required attribute + * + * @return bool + */ + public function getRequired() + { + return $this->required; + } + /** * Returns the nested json element based on the type of entity passed * diff --git a/src/Magento/FunctionalTestingFramework/DataGenerator/Util/JsonObjectExtractor.php b/src/Magento/FunctionalTestingFramework/DataGenerator/Util/JsonObjectExtractor.php index 6f30192b9..ffec6c616 100644 --- a/src/Magento/FunctionalTestingFramework/DataGenerator/Util/JsonObjectExtractor.php +++ b/src/Magento/FunctionalTestingFramework/DataGenerator/Util/JsonObjectExtractor.php @@ -71,6 +71,7 @@ public function extractJsonObject($jsonObjectArray) $jsonDefKey, $dataType, JsonObjectExtractor::JSON_OBJECT_OBJ_NAME, + $jsonObjectArray[JsonDefinitionObjectHandler::ENTITY_OPERATION_REQUIRED] ?? null, $nestedJsonElements, $jsonMetadata ); @@ -89,7 +90,8 @@ private function extractJsonEntries(&$jsonMetadata, $jsonEntryArray) $jsonMetadata[] = new JsonElement( $jsonEntryType[JsonDefinitionObjectHandler::ENTITY_OPERATION_ENTRY_KEY], $jsonEntryType[JsonDefinitionObjectHandler::ENTITY_OPERATION_ENTRY_VALUE], - JsonDefinitionObjectHandler::ENTITY_OPERATION_ENTRY + JsonDefinitionObjectHandler::ENTITY_OPERATION_ENTRY, + $jsonEntryType[JsonDefinitionObjectHandler::ENTITY_OPERATION_REQUIRED] ?? null ); } } @@ -122,6 +124,7 @@ private function extractJsonArrays(&$jsonArrayData, $jsonArrayArray) $jsonEntryType[JsonDefinitionObjectHandler::ENTITY_OPERATION_ARRAY_KEY], $jsonElementValue, JsonDefinitionObjectHandler::ENTITY_OPERATION_ARRAY, + $jsonEntryType[JsonDefinitionObjectHandler::ENTITY_OPERATION_REQUIRED] ?? null, $nestedJsonElements ); } diff --git a/src/Magento/FunctionalTestingFramework/DataGenerator/etc/dataOperation.xsd b/src/Magento/FunctionalTestingFramework/DataGenerator/etc/dataOperation.xsd index 52e144f22..2c4fcabc7 100644 --- a/src/Magento/FunctionalTestingFramework/DataGenerator/etc/dataOperation.xsd +++ b/src/Magento/FunctionalTestingFramework/DataGenerator/etc/dataOperation.xsd @@ -31,12 +31,14 @@ + + @@ -47,6 +49,7 @@ + From dbc03a9aec473b9103e20d12fde0475e4df25a44 Mon Sep 17 00:00:00 2001 From: Ian Meron Date: Tue, 19 Sep 2017 10:00:52 -0500 Subject: [PATCH 07/11] MQE-364:[Data Input] Metadata nest jsonObject not working as expected. - add method for parsing nested jsonObjects - add method for root level jsonObjects --- .../DataGenerator/Api/ApiExecutor.php | 24 ++++++++++++++++++- .../Util/JsonObjectExtractor.php | 2 +- 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/src/Magento/FunctionalTestingFramework/DataGenerator/Api/ApiExecutor.php b/src/Magento/FunctionalTestingFramework/DataGenerator/Api/ApiExecutor.php index afd9fa351..2cad2bf16 100644 --- a/src/Magento/FunctionalTestingFramework/DataGenerator/Api/ApiExecutor.php +++ b/src/Magento/FunctionalTestingFramework/DataGenerator/Api/ApiExecutor.php @@ -161,8 +161,10 @@ private function convertJsonArray($entityObject, $jsonArrayMetadata) foreach ($jsonArrayMetadata as $jsonElement) { if ($jsonElement->getType() == JsonObjectExtractor::JSON_OBJECT_OBJ_NAME) { + $entityObj = $this->resolveJsonObjectAndEntityData($entityObject, $jsonElement->getValue()); $jsonArray[$jsonElement->getValue()] = - $this->convertJsonArray($entityObject, $jsonElement->getNestedMetadata()); + $this->convertJsonArray($entityObj, $jsonElement->getNestedMetadata()); + continue; } $jsonElementType = $jsonElement->getValue(); @@ -222,6 +224,25 @@ private function convertJsonArray($entityObject, $jsonArrayMetadata) return $jsonArray; } + /** + * This function does a comparison of the entity object being matched to the json element. If there is a mismatch in + * type we attempt to use a nested entity, if the entities are properly matched, we simply return the object. + * + * @param EntityDataObject $entityObject + * @param string $jsonElementValue + * @return EntityDataObject|null + */ + private function resolveJsonObjectAndEntityData($entityObject, $jsonElementValue) + { + if ($jsonElementValue != $entityObject->getType()) { + // if we have a mismatch attempt to retrieve linked data and return just the first linkage + $linkName = $entityObject->getLinkedEntitiesOfType($jsonElementValue)[0]; + return DataObjectHandler::getInstance()->getObject($linkName); + } + + return $entityObject; + } + /** * Resolves JsonObjects and pre-defined metadata (in other operation.xml file) referenced by the json metadata * @@ -233,6 +254,7 @@ private function resolveNonPrimitiveElement($entityName, $jsonElement) { $linkedEntityObj = $this->resolveLinkedEntityObject($entityName); + // in array case if (!empty($jsonElement->getNestedJsonElement($jsonElement->getValue())) && $jsonElement->getType() == 'array' ) { diff --git a/src/Magento/FunctionalTestingFramework/DataGenerator/Util/JsonObjectExtractor.php b/src/Magento/FunctionalTestingFramework/DataGenerator/Util/JsonObjectExtractor.php index ffec6c616..f5c80065a 100644 --- a/src/Magento/FunctionalTestingFramework/DataGenerator/Util/JsonObjectExtractor.php +++ b/src/Magento/FunctionalTestingFramework/DataGenerator/Util/JsonObjectExtractor.php @@ -58,7 +58,7 @@ public function extractJsonObject($jsonObjectArray) if (array_key_exists(JsonObjectExtractor::JSON_OBJECT_OBJ_NAME, $jsonObjectArray)) { foreach ($jsonObjectArray[JsonObjectExtractor::JSON_OBJECT_OBJ_NAME] as $jsonObject) { $nestedJsonElement = $this->extractJsonObject($jsonObject); - $nestedJsonElements[$nestedJsonElement->getKey()] = $nestedJsonElement; + $jsonMetadata[] = $nestedJsonElement; } } From 779735f276674063423f794b4868aeefa4319228 Mon Sep 17 00:00:00 2001 From: Kevin Kozan Date: Wed, 20 Sep 2017 12:58:55 -0500 Subject: [PATCH 08/11] MQE-247 --- .../Exceptions/TestReferenceException.php | 23 +++++++++++++ .../Page/Objects/SectionObject.php | 18 ++++++++-- .../Test/Objects/ActionObject.php | 10 ++++-- .../Util/TestGenerator.php | 34 ++++++++++++++----- 4 files changed, 72 insertions(+), 13 deletions(-) create mode 100644 src/Magento/FunctionalTestingFramework/Exceptions/TestReferenceException.php diff --git a/src/Magento/FunctionalTestingFramework/Exceptions/TestReferenceException.php b/src/Magento/FunctionalTestingFramework/Exceptions/TestReferenceException.php new file mode 100644 index 000000000..6bc3bc31f --- /dev/null +++ b/src/Magento/FunctionalTestingFramework/Exceptions/TestReferenceException.php @@ -0,0 +1,23 @@ +elements; } + /** + * Checks to see if this section contains any element by the name of elementName + * @param $elementName + * @return bool + */ + public function hasElement($elementName) + { + return array_key_exists($elementName, $this->elements); + } + /** * Given the name of an element, returns the element object * * @param string $elementName - * @return ElementObject + * @return ElementObject | null */ public function getElement($elementName) { - return $this->elements[$elementName]; + if ($this->hasElement($elementName)) { + return $this->elements[$elementName]; + } + + return null; } } diff --git a/src/Magento/FunctionalTestingFramework/Test/Objects/ActionObject.php b/src/Magento/FunctionalTestingFramework/Test/Objects/ActionObject.php index 40bd1d17d..fde33b8c9 100644 --- a/src/Magento/FunctionalTestingFramework/Test/Objects/ActionObject.php +++ b/src/Magento/FunctionalTestingFramework/Test/Objects/ActionObject.php @@ -12,6 +12,7 @@ use Magento\FunctionalTestingFramework\Page\Objects\SectionObject; use Magento\FunctionalTestingFramework\Page\Handlers\PageObjectHandler; use Magento\FunctionalTestingFramework\Page\Handlers\SectionObjectHandler; +use Magento\FunctionalTestingFramework\Exceptions\TestReferenceException; /** * Class ActionObject @@ -321,6 +322,9 @@ private function findAndReplaceReferences($objectHandler, $inputString) break; case SectionObject::class: list(,$objField) = $this->stripAndSplitReference($match); + if ($obj->getElement($objField) == null) { + throw new TestReferenceException("Could not resolve entity reference " . $inputString); + } $parameterized = $obj->getElement($objField)->isParameterized(); $replacement = $obj->getElement($objField)->getLocator(); $this->timeout = $obj->getElement($objField)->getTimeout(); @@ -347,7 +351,7 @@ private function findAndReplaceReferences($objectHandler, $inputString) if ($replacement == null && get_class($objectHandler) != DataObjectHandler::class) { return $this->findAndReplaceReferences(DataObjectHandler::getInstance(), $outputString); } elseif ($replacement == null) { - throw new \Exception("Could not resolve entity reference " . $inputString); + throw new TestReferenceException("Could not resolve entity reference " . $inputString); } //If Page or Section's Element is has parameterized = true attribute, attempt to do parameter replacement. @@ -373,12 +377,12 @@ private function matchParameterReferences($reference, $parameters) { preg_match_all('/{{[\w.]+}}/', $reference, $varMatches); if (count($varMatches[0]) > count($parameters)) { - throw new \Exception( + throw new TestReferenceException( "Parameter Resolution Failed: Not enough parameters given for reference " . $reference . ". Parameters Given: " . implode(",", $parameters) ); } elseif (count($varMatches[0]) < count($parameters)) { - throw new \Exception( + throw new TestReferenceException( "Parameter Resolution Failed: Too many parameters given for reference " . $reference . ". Parameters Given: " . implode(",", $parameters) ); diff --git a/src/Magento/FunctionalTestingFramework/Util/TestGenerator.php b/src/Magento/FunctionalTestingFramework/Util/TestGenerator.php index c9226abb0..055e4427a 100644 --- a/src/Magento/FunctionalTestingFramework/Util/TestGenerator.php +++ b/src/Magento/FunctionalTestingFramework/Util/TestGenerator.php @@ -8,6 +8,7 @@ use FilesystemIterator; use Magento\FunctionalTestingFramework\DataGenerator\Objects\EntityDataObject; +use Magento\FunctionalTestingFramework\Exceptions\TestReferenceException; use Magento\FunctionalTestingFramework\Test\Handlers\CestObjectHandler; use Magento\FunctionalTestingFramework\Test\Objects\ActionObject; use Magento\FunctionalTestingFramework\DataGenerator\Handlers\DataObjectHandler; @@ -63,7 +64,7 @@ private function rmdirRecursive($directory) { $it = new RecursiveDirectoryIterator($directory, FilesystemIterator::SKIP_DOTS); - while($it->valid()) { + while ($it->valid()) { $path = $directory . DIRECTORY_SEPARATOR . $it->getFilename(); if ($it->isDir()) { $this->rmDirRecursive($path); @@ -85,7 +86,7 @@ private function rmdirRecursive($directory) public static function getInstance() { if (!self::$testGenerator) { - self::$testGenerator = new TestGenerator(TESTS_MODULE_PATH . DIRECTORY_SEPARATOR . "_generated"); + self::$testGenerator = new TestGenerator(TESTS_MODULE_PATH . DIRECTORY_SEPARATOR . "_generated"); } return self::$testGenerator; @@ -152,8 +153,12 @@ private function assembleCestPhp($cestObject) $classAnnotationsPhp = $this->generateClassAnnotationsPhp($cestObject->getAnnotations()); $className = $cestObject->getName(); $className = str_replace(' ', '', $className); - $hookPhp = $this->generateHooksPhp($cestObject->getHooks()); - $testsPhp = $this->generateTestsPhp($cestObject->getTests()); + try { + $hookPhp = $this->generateHooksPhp($cestObject->getHooks()); + $testsPhp = $this->generateTestsPhp($cestObject->getTests()); + } catch (TestReferenceException $e) { + throw new TestReferenceException($e->getMessage(). " in Cest \"" . $cestObject->getName() . "\""); + } $cestPhp = "addUniquenessFunctionCall($param); } - $parameterArray = '[' . implode(',', $paramsWithUniqueness) .']'; + $parameterArray = '[' . implode(',', $paramsWithUniqueness) . ']'; } if (isset($customActionAttributes['requiredAction'])) { @@ -865,7 +870,15 @@ private function generateHooksPhp($hookObjects) } } - $steps = $this->generateStepsPhp($hookObject->getActions(), $hookObject->getCustomData(), $createData); + try { + $steps = $this->generateStepsPhp( + $hookObject->getActions(), + $hookObject->getCustomData(), + $createData + ); + } catch (TestReferenceException $e) { + throw new TestReferenceException($e->getMessage() . " in Element \"" . $type . "\""); + } if ($type == "after") { $hooks .= sprintf("\tpublic function _after(%s)\n", $dependencies); @@ -988,7 +1001,11 @@ private function generateTestsPhp($testsObject) $testName = str_replace(' ', '', $testName); $testAnnotations = $this->generateTestAnnotationsPhp($test->getAnnotations()); $dependencies = 'AcceptanceTester $I'; - $steps = $this->generateStepsPhp($test->getOrderedActions(), $test->getCustomData()); + try { + $steps = $this->generateStepsPhp($test->getOrderedActions(), $test->getCustomData()); + } catch (TestReferenceException $e) { + throw new TestReferenceException($e->getMessage() . " in Test \"" . $test->getName() . "\""); + } $testPhp .= $testAnnotations; $testPhp .= sprintf("\tpublic function %s(%s)\n", $testName, $dependencies); @@ -1014,7 +1031,7 @@ private function addUniquenessFunctionCall($input) { $output = ''; - preg_match('/' . EntityDataObject::CEST_UNIQUE_FUNCTION .'\("[\w]+"\)/', $input, $matches); + preg_match('/' . EntityDataObject::CEST_UNIQUE_FUNCTION . '\("[\w]+"\)/', $input, $matches); if (!empty($matches)) { $parts = preg_split('/' . EntityDataObject::CEST_UNIQUE_FUNCTION . '\("[\w]+"\)/', $input, -1); for ($i = 0; $i < count($parts); $i++) { @@ -1082,6 +1099,7 @@ private function addDollarSign($input) } // @codingStandardsIgnoreStart + /** * Wrap parameters into a function call. * From a81ce7dc60c927334f0d35540371a34f585ad5a3 Mon Sep 17 00:00:00 2001 From: Ian Meron Date: Wed, 20 Sep 2017 13:18:21 -0500 Subject: [PATCH 09/11] MQE-371:[Framework] CestHookObject does order or replace {{}} references - add support for order and ref replacement in before/after blocks --- .../Test/Objects/ActionGroupObject.php | 2 +- .../Test/Objects/CestHookObject.php | 5 ++- .../Test/Objects/TestObject.php | 30 +-------------- .../Test/Util/ActionMergeUtil.php | 37 ++++++++++++++++++- 4 files changed, 41 insertions(+), 33 deletions(-) diff --git a/src/Magento/FunctionalTestingFramework/Test/Objects/ActionGroupObject.php b/src/Magento/FunctionalTestingFramework/Test/Objects/ActionGroupObject.php index df30cc213..e51117e36 100644 --- a/src/Magento/FunctionalTestingFramework/Test/Objects/ActionGroupObject.php +++ b/src/Magento/FunctionalTestingFramework/Test/Objects/ActionGroupObject.php @@ -65,7 +65,7 @@ public function getSteps($arguments) $args = array_merge($args, $arguments); } - return $mergeUtil->mergeStepsAndInsertWaits($this->getResolvedActionsWithArgs($args)); + return $mergeUtil->resolveActionSteps($this->getResolvedActionsWithArgs($args), true); } /** diff --git a/src/Magento/FunctionalTestingFramework/Test/Objects/CestHookObject.php b/src/Magento/FunctionalTestingFramework/Test/Objects/CestHookObject.php index 3837aaf69..7d62b32ec 100644 --- a/src/Magento/FunctionalTestingFramework/Test/Objects/CestHookObject.php +++ b/src/Magento/FunctionalTestingFramework/Test/Objects/CestHookObject.php @@ -6,6 +6,8 @@ namespace Magento\FunctionalTestingFramework\Test\Objects; +use Magento\FunctionalTestingFramework\Test\Util\ActionMergeUtil; + /** * Class CestHookObject */ @@ -61,7 +63,8 @@ public function getType() */ public function getActions() { - return $this->actions; + $mergeUtil = new ActionMergeUtil(); + return $mergeUtil->resolveActionSteps($this->actions); } /** diff --git a/src/Magento/FunctionalTestingFramework/Test/Objects/TestObject.php b/src/Magento/FunctionalTestingFramework/Test/Objects/TestObject.php index d4b79075f..97865adb1 100644 --- a/src/Magento/FunctionalTestingFramework/Test/Objects/TestObject.php +++ b/src/Magento/FunctionalTestingFramework/Test/Objects/TestObject.php @@ -99,34 +99,6 @@ public function getCustomData() public function getOrderedActions() { $mergeUtil = new ActionMergeUtil(); - $mergedSteps = $mergeUtil->mergeStepsAndInsertWaits($this->parsedSteps); - return $this->extractActionGroups($mergedSteps); - } - - /** - * Method to insert action group references into step flow - * - * @param array $mergedSteps - * @return array - */ - private function extractActionGroups($mergedSteps) - { - $newOrderedList = []; - - foreach ($mergedSteps as $key => $mergedStep) { - /**@var ActionObject $mergedStep**/ - if ($mergedStep->getType() == ActionObjectExtractor::ACTION_GROUP_TAG) { - $actionGroup = ActionGroupObjectHandler::getInstance()->getObject( - $mergedStep->getCustomActionAttributes()[ActionObjectExtractor::ACTION_GROUP_REF] - ); - $args = $mergedStep->getCustomActionAttributes()[ActionObjectExtractor::ACTION_GROUP_ARGUMENTS] ?? null; - $actionsToMerge = $actionGroup->getSteps($args); - $newOrderedList = $newOrderedList + $actionsToMerge; - } else { - $newOrderedList[$key] = $mergedStep; - } - } - - return $newOrderedList; + return $mergeUtil->resolveActionSteps($this->parsedSteps); } } diff --git a/src/Magento/FunctionalTestingFramework/Test/Util/ActionMergeUtil.php b/src/Magento/FunctionalTestingFramework/Test/Util/ActionMergeUtil.php index 92ee032b3..133ace223 100644 --- a/src/Magento/FunctionalTestingFramework/Test/Util/ActionMergeUtil.php +++ b/src/Magento/FunctionalTestingFramework/Test/Util/ActionMergeUtil.php @@ -6,6 +6,7 @@ namespace Magento\FunctionalTestingFramework\Test\Util; +use Magento\FunctionalTestingFramework\Test\Handlers\ActionGroupObjectHandler; use Magento\FunctionalTestingFramework\Test\Objects\ActionObject; /** @@ -44,14 +45,46 @@ public function __construct() * Method to execute merge of steps and insert wait steps. * * @param array $parsedSteps + * @param bool $skipActionGroupResolution * @return array */ - public function mergeStepsAndInsertWaits($parsedSteps) + public function resolveActionSteps($parsedSteps, $skipActionGroupResolution = false) { $this->mergeActions($parsedSteps); $this->insertWaits(); - return $this->orderedSteps; + if ($skipActionGroupResolution) { + return $this->orderedSteps; + } + + return $this->resolveActionGroups($this->orderedSteps); + } + + /** + * Method to resolve action group references and insert relevant actions into step flow + * + * @param array $mergedSteps + * @return array + */ + private function resolveActionGroups($mergedSteps) + { + $newOrderedList = []; + + foreach ($mergedSteps as $key => $mergedStep) { + /**@var ActionObject $mergedStep**/ + if ($mergedStep->getType() == ActionObjectExtractor::ACTION_GROUP_TAG) { + $actionGroup = ActionGroupObjectHandler::getInstance()->getObject( + $mergedStep->getCustomActionAttributes()[ActionObjectExtractor::ACTION_GROUP_REF] + ); + $args = $mergedStep->getCustomActionAttributes()[ActionObjectExtractor::ACTION_GROUP_ARGUMENTS] ?? null; + $actionsToMerge = $actionGroup->getSteps($args); + $newOrderedList = $newOrderedList + $actionsToMerge; + } else { + $newOrderedList[$key] = $mergedStep; + } + } + + return $newOrderedList; } /** From b8f49d03815a7da6b273920cbdd4c814ea8b4b21 Mon Sep 17 00:00:00 2001 From: Ian Meron Date: Wed, 20 Sep 2017 13:41:11 -0500 Subject: [PATCH 10/11] MQE-349:[PageObjects] Need to add customized function for conditional click - add supporting logic for conditional click behavior - add conditionalClick schema to test xsd --- .../Module/MagentoWebDriver.php | 37 +++++++++++++------ .../Test/Objects/ActionObject.php | 16 +++++--- .../Test/etc/sampleCest.xml | 1 + .../Test/etc/testSchema.xsd | 14 +++++++ .../Util/TestGenerator.php | 14 +++++++ 5 files changed, 66 insertions(+), 16 deletions(-) diff --git a/src/Magento/FunctionalTestingFramework/Module/MagentoWebDriver.php b/src/Magento/FunctionalTestingFramework/Module/MagentoWebDriver.php index 660055d18..2bca979ca 100644 --- a/src/Magento/FunctionalTestingFramework/Module/MagentoWebDriver.php +++ b/src/Magento/FunctionalTestingFramework/Module/MagentoWebDriver.php @@ -99,17 +99,6 @@ public function _getCurrentUri() return Uri::retrieveUri($url); } - /** - * Returns an array of Elements. - * - * @param string $locator - * @return array - */ - public function findElement($locator) - { - return $this->_findElements($locator); - } - /** * Login Magento Admin with given username and password. * @@ -285,6 +274,32 @@ public function scrollToTopOfPage() $this->executeJS('window.scrollTo(0,0);'); } + /** + * Conditional click for an area that should be visible + * + * @param string $selector + * @param string dependentSelector + * @param bool $visible + */ + public function conditionalClick($selector, $dependentSelector, $visible) + { + $el = $this->_findElements($dependentSelector); + if (sizeof($el) > 1) { + throw new \Exception("more than one element matches selector " . $selector); + } + + $clickCondition = null; + if ($visible) { + $clickCondition = !empty($el) && $el[0]->isDisplayed(); + } else { + $clickCondition = empty($el) || !$el[0]->isDisplayed(); + } + + if ($clickCondition) { + $this->click($selector); + } + } + /** * Override for _failed method in Codeception method. Adds png and html attachments to allure report * following parent execution of test failure processing. diff --git a/src/Magento/FunctionalTestingFramework/Test/Objects/ActionObject.php b/src/Magento/FunctionalTestingFramework/Test/Objects/ActionObject.php index fde33b8c9..cb58c361b 100644 --- a/src/Magento/FunctionalTestingFramework/Test/Objects/ActionObject.php +++ b/src/Magento/FunctionalTestingFramework/Test/Objects/ActionObject.php @@ -20,6 +20,7 @@ class ActionObject { const DATA_ENABLED_ATTRIBUTES = ["userInput", "parameterArray"]; + const SELECTOR_ENABLED_ATTRIBUTES = ['selector', 'dependentSelector']; const MERGE_ACTION_ORDER_AFTER = 'after'; const ACTION_ATTRIBUTE_URL = 'url'; const ACTION_ATTRIBUTE_SELECTOR = 'selector'; @@ -189,15 +190,20 @@ public function resolveReferences() */ private function resolveSelectorReferenceAndTimeout() { - if (!array_key_exists(ActionObject::ACTION_ATTRIBUTE_SELECTOR, $this->actionAttributes)) { + $actionAttributeKeys = array_keys($this->actionAttributes); + $relevantSelectorAttributes = array_intersect($actionAttributeKeys, ActionObject::SELECTOR_ENABLED_ATTRIBUTES); + + if (empty($relevantSelectorAttributes)) { return; } - $selector = $this->actionAttributes[ActionObject::ACTION_ATTRIBUTE_SELECTOR]; + foreach ($relevantSelectorAttributes as $selectorAttribute) { + $selector = $this->actionAttributes[$selectorAttribute]; - $replacement = $this->findAndReplaceReferences(SectionObjectHandler::getInstance(), $selector); - if ($replacement) { - $this->resolvedCustomAttributes[ActionObject::ACTION_ATTRIBUTE_SELECTOR] = $replacement; + $replacement = $this->findAndReplaceReferences(SectionObjectHandler::getInstance(), $selector); + if ($replacement) { + $this->resolvedCustomAttributes[$selectorAttribute] = $replacement; + } } } diff --git a/src/Magento/FunctionalTestingFramework/Test/etc/sampleCest.xml b/src/Magento/FunctionalTestingFramework/Test/etc/sampleCest.xml index 8bcec49ae..2ae9e1260 100644 --- a/src/Magento/FunctionalTestingFramework/Test/etc/sampleCest.xml +++ b/src/Magento/FunctionalTestingFramework/Test/etc/sampleCest.xml @@ -21,6 +21,7 @@ + diff --git a/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd b/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd index 945f2e133..94d6d5a7a 100644 --- a/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd +++ b/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd @@ -84,6 +84,7 @@ + @@ -352,6 +353,19 @@ + + + + + + + + + + + + + diff --git a/src/Magento/FunctionalTestingFramework/Util/TestGenerator.php b/src/Magento/FunctionalTestingFramework/Util/TestGenerator.php index 055e4427a..67846a3c4 100644 --- a/src/Magento/FunctionalTestingFramework/Util/TestGenerator.php +++ b/src/Magento/FunctionalTestingFramework/Util/TestGenerator.php @@ -348,6 +348,8 @@ private function generateStepsPhp($stepsObject, $stepsData, $hookObject = false) $value = null; $button = null; $parameter = null; + $dependentSelector = null; + $visible = null; if (isset($customActionAttributes['returnVariable'])) { $returnVariable = $customActionAttributes['returnVariable']; @@ -450,6 +452,15 @@ private function generateStepsPhp($stepsObject, $stepsData, $hookObject = false) $parameter = $this->wrapWithDoubleQuotes($customActionAttributes['parameter']); } + + if (isset($customActionAttributes['dependentSelector'])) { + $dependentSelector = $this->wrapWithDoubleQuotes($customActionAttributes['dependentSelector']); + } + + if (isset($customActionAttributes['visible'])) { + $visible = $customActionAttributes['visible']; + } + switch ($actionName) { case "createData": $entity = $customActionAttributes['entity']; @@ -757,6 +768,9 @@ private function generateStepsPhp($stepsObject, $stepsData, $hookObject = false) // TODO: Need to fix xml parser to allow parsing html. $testSteps .= $this->wrapFunctionCall($actor, $actionName, $html); break; + case "conditionalClick": + $testSteps .= $this->wrapFunctionCall($actor, $actionName, $selector, $dependentSelector, $visible); + break; default: if ($returnVariable) { $testSteps .= $this->wrapFunctionCallWithReturnValue( From e537ce7547e445e71b7d869ba814f13177a78f41 Mon Sep 17 00:00:00 2001 From: Alex Kolesnyk Date: Wed, 20 Sep 2017 22:03:11 +0300 Subject: [PATCH 11/11] MQE-349:[PageObjects] Need to add customized function for conditional click - fix static test issues --- .../DataGenerator/Objects/JsonElement.php | 4 ++-- .../Exceptions/TestReferenceException.php | 1 - .../Page/Objects/SectionObject.php | 2 +- .../FunctionalTestingFramework/Util/TestGenerator.php | 3 ++- src/Magento/FunctionalTestingFramework/Util/TestManifest.php | 5 ++--- 5 files changed, 7 insertions(+), 8 deletions(-) diff --git a/src/Magento/FunctionalTestingFramework/DataGenerator/Objects/JsonElement.php b/src/Magento/FunctionalTestingFramework/DataGenerator/Objects/JsonElement.php index 975d44f06..a2af57341 100644 --- a/src/Magento/FunctionalTestingFramework/DataGenerator/Objects/JsonElement.php +++ b/src/Magento/FunctionalTestingFramework/DataGenerator/Objects/JsonElement.php @@ -54,9 +54,9 @@ class JsonElement * @param string $key * @param string $value * @param string $type - * @param array $nestedElements * @param bool $required - * @param array $nestedMetadata + * @param array $nestedElements + * @param null|array $nestedMetadata */ public function __construct($key, $value, $type, $required, $nestedElements = [], $nestedMetadata = null) { diff --git a/src/Magento/FunctionalTestingFramework/Exceptions/TestReferenceException.php b/src/Magento/FunctionalTestingFramework/Exceptions/TestReferenceException.php index 6bc3bc31f..1fd2e4f67 100644 --- a/src/Magento/FunctionalTestingFramework/Exceptions/TestReferenceException.php +++ b/src/Magento/FunctionalTestingFramework/Exceptions/TestReferenceException.php @@ -19,5 +19,4 @@ public function __construct($message) { parent::__construct($message); } - } diff --git a/src/Magento/FunctionalTestingFramework/Page/Objects/SectionObject.php b/src/Magento/FunctionalTestingFramework/Page/Objects/SectionObject.php index 7b36ec116..121d3e6ca 100644 --- a/src/Magento/FunctionalTestingFramework/Page/Objects/SectionObject.php +++ b/src/Magento/FunctionalTestingFramework/Page/Objects/SectionObject.php @@ -58,7 +58,7 @@ public function getElements() /** * Checks to see if this section contains any element by the name of elementName - * @param $elementName + * @param string $elementName * @return bool */ public function hasElement($elementName) diff --git a/src/Magento/FunctionalTestingFramework/Util/TestGenerator.php b/src/Magento/FunctionalTestingFramework/Util/TestGenerator.php index 67846a3c4..2798329bc 100644 --- a/src/Magento/FunctionalTestingFramework/Util/TestGenerator.php +++ b/src/Magento/FunctionalTestingFramework/Util/TestGenerator.php @@ -34,6 +34,7 @@ class TestGenerator /** * TestGenerator constructor. + * @param string $exportDir */ private function __construct($exportDir) { @@ -59,6 +60,7 @@ private function setupExportDir() * Takes a directory path and recursively deletes all files and folders. * * @param string $directory + * @return void */ private function rmdirRecursive($directory) { @@ -452,7 +454,6 @@ private function generateStepsPhp($stepsObject, $stepsData, $hookObject = false) $parameter = $this->wrapWithDoubleQuotes($customActionAttributes['parameter']); } - if (isset($customActionAttributes['dependentSelector'])) { $dependentSelector = $this->wrapWithDoubleQuotes($customActionAttributes['dependentSelector']); } diff --git a/src/Magento/FunctionalTestingFramework/Util/TestManifest.php b/src/Magento/FunctionalTestingFramework/Util/TestManifest.php index c8d635881..2545141c8 100644 --- a/src/Magento/FunctionalTestingFramework/Util/TestManifest.php +++ b/src/Magento/FunctionalTestingFramework/Util/TestManifest.php @@ -49,12 +49,11 @@ public function recordCest($cestName, $tests) { $fileResource = fopen($this->filePath, 'a'); - foreach ($tests as $test) - { + foreach ($tests as $test) { $line = $this->relativeDirPath . DIRECTORY_SEPARATOR . $cestName . '.php:' . $test->getName(); fwrite($fileResource, $line ."\n"); } fclose($fileResource); } -} \ No newline at end of file +}