From 7669e578904d6d0ebf5eb1a0033f00aa5b09b254 Mon Sep 17 00:00:00 2001 From: Ji Lu Date: Fri, 15 Sep 2017 16:38:47 -0500 Subject: [PATCH 01/19] MQE-326: allow data persistence to custom stores. --- .../DataGenerator/Api/ApiExecutor.php | 12 +++- .../DataGenerator/Api/EntityApiHandler.php | 25 ++++++-- .../DataGenerator/Objects/JsonDefinition.php | 45 ++++++++------ .../DataGenerator/etc/dataOperation.xsd | 1 + .../Test/etc/testSchema.xsd | 2 + .../Util/TestGenerator.php | 59 ++++++++++--------- 6 files changed, 91 insertions(+), 53 deletions(-) diff --git a/src/Magento/FunctionalTestingFramework/DataGenerator/Api/ApiExecutor.php b/src/Magento/FunctionalTestingFramework/DataGenerator/Api/ApiExecutor.php index ee0f6eb81..68dfc4666 100644 --- a/src/Magento/FunctionalTestingFramework/DataGenerator/Api/ApiExecutor.php +++ b/src/Magento/FunctionalTestingFramework/DataGenerator/Api/ApiExecutor.php @@ -57,16 +57,25 @@ class ApiExecutor */ private static $entitySequences = []; + /** + * Store code in web api rest url. + * + * @var string + */ + private $storeCode; + /** * ApiSubObject constructor. * @param string $operation * @param EntityDataObject $entityObject * @param array $dependentEntities + * @param string $storeCode */ - public function __construct($operation, $entityObject, $dependentEntities = null) + public function __construct($operation, $entityObject, $dependentEntities = null, $storeCode = 'default') { $this->operation = $operation; $this->entityObject = $entityObject; + $this->storeCode = $storeCode; if ($dependentEntities != null) { foreach ($dependentEntities as $entity) { $this->dependentEntities[$entity->getName()] = $entity; @@ -86,6 +95,7 @@ public function __construct($operation, $entityObject, $dependentEntities = null */ public function executeRequest() { + $this->jsonDefinition->setStoreCode($this->storeCode); $apiClientUrl = $this->jsonDefinition->getApiUrl(); $matchedParams = []; diff --git a/src/Magento/FunctionalTestingFramework/DataGenerator/Api/EntityApiHandler.php b/src/Magento/FunctionalTestingFramework/DataGenerator/Api/EntityApiHandler.php index 8116dbd87..898fce8f8 100644 --- a/src/Magento/FunctionalTestingFramework/DataGenerator/Api/EntityApiHandler.php +++ b/src/Magento/FunctionalTestingFramework/DataGenerator/Api/EntityApiHandler.php @@ -34,6 +34,13 @@ class EntityApiHandler */ private $dependentObjects = []; + /** + * Store code in web api rest url. + * + * @var string + */ + private $storeCode; + /** * ApiPersistenceHandler constructor. * @param EntityDataObject $entityObject @@ -43,15 +50,21 @@ public function __construct($entityObject, $dependentObjects = null) { $this->entityObject = clone $entityObject; $this->dependentObjects = $dependentObjects; + $this->storeCode = 'default'; } /** * Function which executes a create request based on specific operation metadata + * + * @param string $storeCode * @return void */ - public function createEntity() + public function createEntity($storeCode = null) { - $apiExecutor = new ApiExecutor('create', $this->entityObject, $this->dependentObjects); + if (!$storeCode) { + $storeCode = $this->storeCode; + } + $apiExecutor = new ApiExecutor('create', $this->entityObject, $this->dependentObjects, $storeCode); $result = $apiExecutor->executeRequest(); $this->createdObject = new EntityDataObject( @@ -66,11 +79,15 @@ public function createEntity() /** * Function which executes a delete request based on specific operation metadata * + * @param string $storeCode * @return string | false */ - public function deleteEntity() + public function deleteEntity($storeCode = null) { - $apiExecutor = new ApiExecutor('delete', $this->createdObject); + if (!$storeCode) { + $storeCode = $this->storeCode; + } + $apiExecutor = new ApiExecutor('delete', $this->createdObject, null, $storeCode); $result = $apiExecutor->executeRequest(); return $result; diff --git a/src/Magento/FunctionalTestingFramework/DataGenerator/Objects/JsonDefinition.php b/src/Magento/FunctionalTestingFramework/DataGenerator/Objects/JsonDefinition.php index f19a2e51e..a50b108bf 100644 --- a/src/Magento/FunctionalTestingFramework/DataGenerator/Objects/JsonDefinition.php +++ b/src/Magento/FunctionalTestingFramework/DataGenerator/Objects/JsonDefinition.php @@ -40,18 +40,18 @@ class JsonDefinition private $apiMethod; /** - * Base URL for the request + * Api request url. * * @var string */ - private $baseUrl; + private $apiUrl; /** * Resource specific URI for the request * * @var string */ - private $apiUrl; + private $apiUri; /** * Authorization path for retrieving a token @@ -81,38 +81,49 @@ class JsonDefinition */ private $jsonMetadata = []; + /** + * Store code in api url. + * + * @var string + */ + private $apiStoreCode; + /** * JsonDefinition constructor. * @param string $name * @param string $operation * @param string $dataType * @param string $apiMethod - * @param string $apiUrl + * @param string $apiUri * @param string $auth * @param array $headers * @param array $params * @param array $jsonMetadata + * @param string $apiStoreCode */ public function __construct( $name, $operation, $dataType, $apiMethod, - $apiUrl, + $apiUri, $auth, $headers, $params, - $jsonMetadata + $jsonMetadata, + $apiStoreCode = 'default' ) { $this->name = $name; $this->operation = $operation; $this->dataType = $dataType; $this->apiMethod = $apiMethod; - $this->baseUrl = $apiUrl; + $this->apiUri = $apiUri; $this->auth = $auth; $this->headers = $headers; $this->params = $params; $this->jsonMetadata = $jsonMetadata; + $this->apiStoreCode = $apiStoreCode; + $this->apiUrl = null; } /** @@ -152,7 +163,7 @@ public function getApiMethod() */ public function getApiUrl() { - $this->cleanApiUrl(); + $this->apiUrl = '/rest/' . $this->apiStoreCode . '/' . trim($this->apiUri, '/'); if (array_key_exists('path', $this->params)) { $this->addPathParam(); @@ -172,7 +183,7 @@ public function getApiUrl() */ public function getAuth() { - return $this->auth; + return '/rest/' . $this->apiStoreCode . '/' . trim($this->auth, '/'); } /** @@ -196,17 +207,14 @@ public function getJsonMetadata() } /** - * Function to validate api format and add "/" char where necessary + * Set store code. * + * @param $newStoreCode * @return void */ - private function cleanApiUrl() + public function setStoreCode($newStoreCode) { - if (substr($this->baseUrl, -1) == "/") { - $this->apiUrl = rtrim($this->baseUrl, "/"); - } else { - $this->apiUrl = $this->baseUrl; - } + $this->apiStoreCode = $newStoreCode; } /** @@ -228,15 +236,14 @@ private function addPathParam() */ private function addQueryParams() { - foreach ($this->params['query'] as $paramName => $paramValue) { - if (!stringContains("?", $this->apiUrl)) { + if (strpos($this->apiUrl, '?') == false) { $this->apiUrl = $this->apiUrl . "?"; } else { $this->apiUrl = $this->apiUrl . "&"; } - $this->apiUrl = $paramName . "=" . $paramValue; + $this->apiUrl = $this->apiUrl . $paramName . "=" . $paramValue; } } } diff --git a/src/Magento/FunctionalTestingFramework/DataGenerator/etc/dataOperation.xsd b/src/Magento/FunctionalTestingFramework/DataGenerator/etc/dataOperation.xsd index 52e144f22..e74923dbc 100644 --- a/src/Magento/FunctionalTestingFramework/DataGenerator/etc/dataOperation.xsd +++ b/src/Magento/FunctionalTestingFramework/DataGenerator/etc/dataOperation.xsd @@ -18,6 +18,7 @@ + diff --git a/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd b/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd index 945f2e133..51c8c783f 100644 --- a/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd +++ b/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd @@ -361,6 +361,7 @@ + @@ -380,6 +381,7 @@ + diff --git a/src/Magento/FunctionalTestingFramework/Util/TestGenerator.php b/src/Magento/FunctionalTestingFramework/Util/TestGenerator.php index 6799e4bb5..41267f01e 100644 --- a/src/Magento/FunctionalTestingFramework/Util/TestGenerator.php +++ b/src/Magento/FunctionalTestingFramework/Util/TestGenerator.php @@ -430,6 +430,29 @@ private function generateStepsPhp($stepsObject, $stepsData, $hookObject = false) } } } + + if ($hookObject) { + $createEntityFunctionCall = sprintf("\t\t\$this->%s->createEntity(", $key); + $entityApiHandlerFunctionCall = sprintf( + "\t\t\$this->%s = new EntityApiHandler($%s", + $key, + $entity + ); + } else { + $createEntityFunctionCall = sprintf("\t\t\$%s->createEntity(", $key); + $entityApiHandlerFunctionCall = sprintf( + "\t\t$%s = new EntityApiHandler($%s", + $key, + $entity + ); + } + + if (isset($customActionAttributes['storeCode'])) { + $createEntityFunctionCall .= sprintf("\"%s\");\n", $customActionAttributes['storeCode']); + } else { + $createEntityFunctionCall .= ");\n"; + } + //If required-entities are defined, reassign dataObject to not overwrite the static definition. //Also, EntityApiHandler needs to be defined with customData array. if (!empty($requiredEntities)) { @@ -445,37 +468,15 @@ private function generateStepsPhp($stepsObject, $stepsData, $hookObject = false) $entity ); - if ($hookObject) { - $testSteps .= sprintf( - "\t\t\$this->%s = new EntityApiHandler($%s, [%s]);\n", - $key, - $entity, - implode(', ', $requiredEntityObjects) - ); - $testSteps .= sprintf("\t\t\$this->%s->createEntity();\n", $key); - } else { - $testSteps .= sprintf( - "\t\t$%s = new EntityApiHandler($%s, [%s]);\n", - $key, - $entity, - implode(', ', $requiredEntityObjects) - ); - $testSteps .= sprintf("\t\t$%s->createEntity();\n", $key); - } + $entityApiHandlerFunctionCall .= sprintf( + ", [%s]);\n", + implode(', ', $requiredEntityObjects) + ); } else { - if ($hookObject) { - $testSteps .= sprintf( - "\t\t\$this->%s = new EntityApiHandler($%s);\n", - $key, - $entity - ); - $testSteps .= sprintf("\t\t\$this->%s->createEntity();\n", $key); - } else { - $testSteps .= sprintf("\t\t$%s = new EntityApiHandler($%s);\n", $key, $entity); - $testSteps .= sprintf("\t\t$%s->createEntity();\n", $key); - } + $entityApiHandlerFunctionCall .= ");\n"; } - + $testSteps .= $entityApiHandlerFunctionCall; + $testSteps .= $createEntityFunctionCall; break; case "deleteData": $key = $customActionAttributes['createDataKey']; From 596f419950b7915903e46e3c1f6502865f0fa9b5 Mon Sep 17 00:00:00 2001 From: Ji Lu Date: Thu, 21 Sep 2017 09:46:13 -0500 Subject: [PATCH 02/19] MQE-369: Implement non web api data persistence. --- etc/di.xml | 18 -- .../Handlers/DataDefinitionObjectHandler.php | 225 ++++++++++++++ .../Handlers/JsonDefinitionObjectHandler.php | 213 ------------- ...{JsonDefinition.php => DataDefinition.php} | 106 +++++-- .../{JsonElement.php => DataElement.php} | 22 +- .../Persist/Curl/AbstractExecutor.php | 45 +++ .../Persist/Curl/AdminExecutor.php | 182 +++++++++++ .../Persist/Curl/WebapiExecutor.php | 140 +++++++++ .../CurlHandler.php} | 212 +++++++------ .../DataPersistHandler.php} | 49 +-- .../Util/DataObjectExtractor.php | 129 ++++++++ .../Util/JsonObjectExtractor.php | 129 -------- .../DataGenerator/etc/dataOperation.xsd | 20 +- .../DataGenerator/etc/sample.xml | 25 -- .../Util/ApiClientUtil.php | 117 ------- .../Util/Protocol/CurlInterface.php | 57 ++++ .../Util/Protocol/CurlTransport.php | 292 ++++++++++++++++++ .../Util/TestGenerator.php | 20 +- 18 files changed, 1329 insertions(+), 672 deletions(-) create mode 100644 src/Magento/FunctionalTestingFramework/DataGenerator/Handlers/DataDefinitionObjectHandler.php delete mode 100644 src/Magento/FunctionalTestingFramework/DataGenerator/Handlers/JsonDefinitionObjectHandler.php rename src/Magento/FunctionalTestingFramework/DataGenerator/Objects/{JsonDefinition.php => DataDefinition.php} (64%) rename src/Magento/FunctionalTestingFramework/DataGenerator/Objects/{JsonElement.php => DataElement.php} (76%) create mode 100644 src/Magento/FunctionalTestingFramework/DataGenerator/Persist/Curl/AbstractExecutor.php create mode 100644 src/Magento/FunctionalTestingFramework/DataGenerator/Persist/Curl/AdminExecutor.php create mode 100644 src/Magento/FunctionalTestingFramework/DataGenerator/Persist/Curl/WebapiExecutor.php rename src/Magento/FunctionalTestingFramework/DataGenerator/{Api/ApiExecutor.php => Persist/CurlHandler.php} (52%) rename src/Magento/FunctionalTestingFramework/DataGenerator/{Api/EntityApiHandler.php => Persist/DataPersistHandler.php} (67%) create mode 100644 src/Magento/FunctionalTestingFramework/DataGenerator/Util/DataObjectExtractor.php delete mode 100644 src/Magento/FunctionalTestingFramework/DataGenerator/Util/JsonObjectExtractor.php delete mode 100644 src/Magento/FunctionalTestingFramework/DataGenerator/etc/sample.xml delete mode 100644 src/Magento/FunctionalTestingFramework/Util/ApiClientUtil.php create mode 100644 src/Magento/FunctionalTestingFramework/Util/Protocol/CurlInterface.php create mode 100644 src/Magento/FunctionalTestingFramework/Util/Protocol/CurlTransport.php diff --git a/etc/di.xml b/etc/di.xml index 081b324fc..ae49b5afc 100644 --- a/etc/di.xml +++ b/etc/di.xml @@ -49,24 +49,6 @@ - - - Magento\FunctionalTestingFramework\Page\Config\Data - - - - - Magento\FunctionalTestingFramework\Block\Config\Data - - - - - - Magento\FunctionalTestingFramework\Generate\GeneratePage - Magento\FunctionalTestingFramework\Generate\GenerateBlock - - - - + Magento\FunctionalTestingFramework\DataProfile\Config\Metadata @@ -180,7 +180,7 @@ Magento\FunctionalTestingFramework\Config\SchemaLocator\Metadata name - key + key key key diff --git a/src/Magento/FunctionalTestingFramework/DataGenerator/Handlers/DataDefinitionObjectHandler.php b/src/Magento/FunctionalTestingFramework/DataGenerator/Handlers/DataDefinitionObjectHandler.php deleted file mode 100644 index af207bc4b..000000000 --- a/src/Magento/FunctionalTestingFramework/DataGenerator/Handlers/DataDefinitionObjectHandler.php +++ /dev/null @@ -1,228 +0,0 @@ -initDataDefinitions(); - } - - return self::$DATA_DEFINITION_OBJECT_HANDLER; - } - - /** - * Returns a DataDefinition object based on name - * - * @param string $dataDefinitionName - * @return DataDefinition - */ - public function getObject($dataDefinitionName) - { - return $this->dataDefinitions[$dataDefinitionName]; - } - - /** - * Returns all data Definition objects - * - * @return array - */ - public function getAllObjects() - { - return $this->dataDefinitions; - } - - /** - * DataDefintionArrayProcessor constructor. - */ - private function __construct() - { - $this->dataDefExtractor = new DataObjectExtractor(); - } - - /** - * This method takes an operation such as create and a data type such as 'customer' and returns the corresponding - * data definition defined in metadata.xml - * - * @param string $operation - * @param string $dataType - * @return DataDefinition - */ - public function getDataDefinition($operation, $dataType) - { - return $this->getObject($operation . $dataType); - } - - /** - * This method reads all dataDefinitions from metadata xml into memory. - * @return void - */ - private function initDataDefinitions() - { - $objectManager = ObjectManagerFactory::getObjectManager(); - $metadataParser = $objectManager->create(OperationMetadataParser::class); - foreach ($metadataParser->readOperationMetadata()[DataDefinitionObjectHandler::ENTITY_OPERATION_ROOT_TAG] as - $dataDefName => $dataDefArray) { - $operation = $dataDefArray[DataDefinitionObjectHandler::ENTITY_OPERATION_TYPE]; - $dataType = $dataDefArray[DataDefinitionObjectHandler::ENTITY_OPERATION_DATA_TYPE]; - $url = $dataDefArray[DataDefinitionObjectHandler::ENTITY_OPERATION_URL] ?? null; - $method = $dataDefArray[DataDefinitionObjectHandler::ENTITY_OPERATION_METHOD] ?? null; - $auth = $dataDefArray[DataDefinitionObjectHandler::ENTITY_OPERATION_AUTH] ?? null; - $storeCode = $dataDefArray[DataDefinitionObjectHandler::ENTITY_OPERATION_STORE_CODE] ?? null; - $successRegex = $dataDefArray[DataDefinitionObjectHandler::ENTITY_OPERATION_SUCCESS_REGEX] ?? null; - $returnRegex = $dataDefArray[DataDefinitionObjectHandler::ENTITY_OPERATION_RETURN_REGEX] ?? null; - $headers = []; - $params = []; - $metaData = []; - - if (array_key_exists(DataDefinitionObjectHandler::ENTITY_OPERATION_HEADER, $dataDefArray)) { - foreach ($dataDefArray[DataDefinitionObjectHandler::ENTITY_OPERATION_HEADER] as $headerEntry) { - if (isset($headerEntry[DataDefinitionObjectHandler::ENTITY_OPERATION_HEADER_VALUE]) - && $headerEntry[DataDefinitionObjectHandler::ENTITY_OPERATION_HEADER_VALUE] !== 'none') { - $headers[] = $headerEntry[DataDefinitionObjectHandler::ENTITY_OPERATION_HEADER_PARAM] . ': ' . - $headerEntry[DataDefinitionObjectHandler::ENTITY_OPERATION_HEADER_VALUE]; - } - } - } - - if (array_key_exists(DataDefinitionObjectHandler::ENTITY_OPERATION_URL_PARAM, $dataDefArray)) { - foreach ($dataDefArray[DataDefinitionObjectHandler::ENTITY_OPERATION_URL_PARAM] as $paramEntry) { - $params[$paramEntry[DataDefinitionObjectHandler::ENTITY_OPERATION_URL_PARAM_TYPE]] - [$paramEntry[DataDefinitionObjectHandler::ENTITY_OPERATION_URL_PARAM_KEY]] = - $paramEntry[DataDefinitionObjectHandler::ENTITY_OPERATION_URL_PARAM_VALUE]; - } - } - - // extract relevant dataObjects as DataElements - if (array_key_exists(DataDefinitionObjectHandler::ENTITY_OPERATION_DATA_OBJECT, $dataDefArray)) { - foreach ($dataDefArray[DataDefinitionObjectHandler::ENTITY_OPERATION_DATA_OBJECT] as $dataObjectArray) { - $metaData[] = $this->dataDefExtractor->extractDataObject($dataObjectArray); - } - } - - //handle loose entries - - if (array_key_exists(DataDefinitionObjectHandler::ENTITY_OPERATION_ENTRY, $dataDefArray)) { - foreach ($dataDefArray[DataDefinitionObjectHandler::ENTITY_OPERATION_ENTRY] as $dataEntryType) { - $metaData[] = new DataElement( - $dataEntryType[DataDefinitionObjectHandler::ENTITY_OPERATION_ENTRY_KEY], - $dataEntryType[DataDefinitionObjectHandler::ENTITY_OPERATION_ENTRY_VALUE], - DataDefinitionObjectHandler::ENTITY_OPERATION_ENTRY, - $dataEntryType[DataDefinitionObjectHandler::ENTITY_OPERATION_REQUIRED] ?? null - ); - } - } - - if (array_key_exists(DataDefinitionObjectHandler::ENTITY_OPERATION_ARRAY, $dataDefArray)) { - foreach ($dataDefArray[DataDefinitionObjectHandler::ENTITY_OPERATION_ARRAY] as $dataEntryType) { - $subMetaData = []; - $value = null; - $type = null; - - if (array_key_exists(DataDefinitionObjectHandler::ENTITY_OPERATION_DATA_OBJECT, $dataEntryType)) { - $nestedDataElement = $this->dataDefExtractor->extractDataObject( - $dataEntryType[DataDefinitionObjectHandler::ENTITY_OPERATION_DATA_OBJECT][0] - ); - $subMetaData[$nestedDataElement->getKey()] = $nestedDataElement; - $value = $nestedDataElement->getValue(); - $type = $nestedDataElement->getKey(); - } else { - $value = $dataEntryType[DataDefinitionObjectHandler::ENTITY_OPERATION_ARRAY_VALUE][0] - [DataDefinitionObjectHandler::ENTITY_OPERATION_ARRAY_VALUE]; - $type = [DataDefinitionObjectHandler::ENTITY_OPERATION_ARRAY_VALUE]; - } - - $metaData[] = new DataElement( - $dataEntryType[DataDefinitionObjectHandler::ENTITY_OPERATION_ARRAY_KEY], - $value, - $type, - $dataEntryType[DataDefinitionObjectHandler::ENTITY_OPERATION_REQUIRED] ?? null, - $subMetaData - ); - } - } - - $this->dataDefinitions[$operation . $dataType] = new DataDefinition( - $dataDefName, - $operation, - $dataType, - $method, - $url, - $auth, - $headers, - $params, - $metaData, - $successRegex, - $returnRegex, - $storeCode - ); - } - } -} diff --git a/src/Magento/FunctionalTestingFramework/DataGenerator/Handlers/OperationDefinitionObjectHandler.php b/src/Magento/FunctionalTestingFramework/DataGenerator/Handlers/OperationDefinitionObjectHandler.php new file mode 100644 index 000000000..4d28caab0 --- /dev/null +++ b/src/Magento/FunctionalTestingFramework/DataGenerator/Handlers/OperationDefinitionObjectHandler.php @@ -0,0 +1,233 @@ +initDataDefinitions(); + } + + return self::$DATA_DEFINITION_OBJECT_HANDLER; + } + + /** + * Returns a OperationDefinitionObject object based on name + * + * @param string $operationDefinitionName + * @return OperationDefinitionObject + */ + public function getObject($operationDefinitionName) + { + return $this->operationDefinitions[$operationDefinitionName]; + } + + /** + * Returns all data Definition objects + * + * @return array + */ + public function getAllObjects() + { + return $this->operationDefinitions; + } + + /** + * DataDefintionArrayProcessor constructor. + */ + private function __construct() + { + $this->dataDefExtractor = new OperationElementExtractor(); + } + + /** + * This method takes an operation such as create and a data type such as 'customer' and returns the corresponding + * data definition defined in metadata.xml + * + * @param string $operation + * @param string $dataType + * @return OperationDefinitionObject + */ + public function getOperationDefinition($operation, $dataType) + { + return $this->getObject($operation . $dataType); + } + + /** + * This method reads all dataDefinitions from metadata xml into memory. + * @return void + */ + private function initDataDefinitions() + { + $objectManager = ObjectManagerFactory::getObjectManager(); + $metadataParser = $objectManager->create(OperationDefinitionParser::class); + foreach ($metadataParser->readOperationMetadata() + [OperationDefinitionObjectHandler::ENTITY_OPERATION_ROOT_TAG] as $dataDefName => $opDefArray) { + $operation = $opDefArray[OperationDefinitionObjectHandler::ENTITY_OPERATION_TYPE]; + $dataType = $opDefArray[OperationDefinitionObjectHandler::ENTITY_OPERATION_DATA_TYPE]; + $url = $opDefArray[OperationDefinitionObjectHandler::ENTITY_OPERATION_URL] ?? null; + $method = $opDefArray[OperationDefinitionObjectHandler::ENTITY_OPERATION_METHOD] ?? null; + $auth = $opDefArray[OperationDefinitionObjectHandler::ENTITY_OPERATION_AUTH] ?? null; + $successRegex = $opDefArray[OperationDefinitionObjectHandler::ENTITY_OPERATION_SUCCESS_REGEX] ?? null; + $returnRegex = $opDefArray[OperationDefinitionObjectHandler::ENTITY_OPERATION_RETURN_REGEX] ?? null; + $contentType = $opDefArray[OperationDefinitionObjectHandler::ENTITY_OPERATION_CONTENT_TYPE][0]['value'] + ?? null; + $headers = []; + $params = []; + $operationElements = []; + + if (array_key_exists(OperationDefinitionObjectHandler::ENTITY_OPERATION_HEADER, $opDefArray)) { + foreach ($opDefArray[OperationDefinitionObjectHandler::ENTITY_OPERATION_HEADER] as $headerEntry) { + if (isset($headerEntry[OperationDefinitionObjectHandler::ENTITY_OPERATION_HEADER_VALUE]) + && $headerEntry[OperationDefinitionObjectHandler::ENTITY_OPERATION_HEADER_VALUE] !== 'none') { + $headers[] = $headerEntry[OperationDefinitionObjectHandler::ENTITY_OPERATION_HEADER_PARAM] + . ': ' + . $headerEntry[OperationDefinitionObjectHandler::ENTITY_OPERATION_HEADER_VALUE]; + } + } + } + + if (array_key_exists(OperationDefinitionObjectHandler::ENTITY_OPERATION_URL_PARAM, $opDefArray)) { + foreach ($opDefArray[OperationDefinitionObjectHandler::ENTITY_OPERATION_URL_PARAM] as $paramEntry) { + $params[$paramEntry[OperationDefinitionObjectHandler::ENTITY_OPERATION_URL_PARAM_TYPE]] + [$paramEntry[OperationDefinitionObjectHandler::ENTITY_OPERATION_URL_PARAM_KEY]] = + $paramEntry[OperationDefinitionObjectHandler::ENTITY_OPERATION_URL_PARAM_VALUE]; + } + } + + // extract relevant OperationObjects as OperationElements + if (array_key_exists(OperationDefinitionObjectHandler::ENTITY_OPERATION_OBJECT, $opDefArray)) { + foreach ($opDefArray[OperationDefinitionObjectHandler::ENTITY_OPERATION_OBJECT] as $opElementArray) { + $operationElements[] = $this->dataDefExtractor->extractOperationElement($opElementArray); + } + } + + //handle loose operation fields + if (array_key_exists(OperationDefinitionObjectHandler::ENTITY_OPERATION_ENTRY, $opDefArray)) { + foreach ($opDefArray[OperationDefinitionObjectHandler::ENTITY_OPERATION_ENTRY] as $operationField) { + $operationElements[] = new OperationElement( + $operationField[OperationDefinitionObjectHandler::ENTITY_OPERATION_ENTRY_KEY], + $operationField[OperationDefinitionObjectHandler::ENTITY_OPERATION_ENTRY_VALUE], + OperationDefinitionObjectHandler::ENTITY_OPERATION_ENTRY, + $operationField[OperationDefinitionObjectHandler::ENTITY_OPERATION_REQUIRED] ?? null + ); + } + } + + // handle loose json arrays + if (array_key_exists(OperationDefinitionObjectHandler::ENTITY_OPERATION_ARRAY, $opDefArray)) { + foreach ($opDefArray[OperationDefinitionObjectHandler::ENTITY_OPERATION_ARRAY] as $operationField) { + $subOperationElements = []; + $value = null; + $type = null; + + if (array_key_exists( + OperationDefinitionObjectHandler::ENTITY_OPERATION_OBJECT, + $operationField + )) { + $nestedDataElement = $this->dataDefExtractor->extractOperationElement( + $operationField[OperationDefinitionObjectHandler::ENTITY_OPERATION_OBJECT][0] + ); + $subOperationElements[$nestedDataElement->getKey()] = $nestedDataElement; + $value = $nestedDataElement->getValue(); + $type = $nestedDataElement->getKey(); + } else { + $value = $operationField[OperationDefinitionObjectHandler::ENTITY_OPERATION_ARRAY_VALUE][0] + [OperationDefinitionObjectHandler::ENTITY_OPERATION_ARRAY_VALUE]; + $type = [OperationDefinitionObjectHandler::ENTITY_OPERATION_ARRAY_VALUE]; + } + + $operationElements[] = new OperationElement( + $operationField[OperationDefinitionObjectHandler::ENTITY_OPERATION_ARRAY_KEY], + $value, + $type, + $dataEntryType[OperationDefinitionObjectHandler::ENTITY_OPERATION_REQUIRED] ?? null, + $subOperationElements + ); + } + } + + $this->operationDefinitions[$operation . $dataType] = new OperationDefinitionObject( + $dataDefName, + $operation, + $dataType, + $method, + $url, + $auth, + $headers, + $params, + $operationElements, + $contentType, + $successRegex, + $returnRegex + ); + } + } +} diff --git a/src/Magento/FunctionalTestingFramework/DataGenerator/Objects/DataDefinition.php b/src/Magento/FunctionalTestingFramework/DataGenerator/Objects/OperationDefinitionObject.php similarity index 76% rename from src/Magento/FunctionalTestingFramework/DataGenerator/Objects/DataDefinition.php rename to src/Magento/FunctionalTestingFramework/DataGenerator/Objects/OperationDefinitionObject.php index b09cfcc1f..8f9ac19a4 100644 --- a/src/Magento/FunctionalTestingFramework/DataGenerator/Objects/DataDefinition.php +++ b/src/Magento/FunctionalTestingFramework/DataGenerator/Objects/OperationDefinitionObject.php @@ -7,10 +7,12 @@ namespace Magento\FunctionalTestingFramework\DataGenerator\Objects; /** - * Class DataDefinition + * Class OperationDefinitionObject */ -class DataDefinition +class OperationDefinitionObject { + const HTTP_CONTENT_TYPE_HEADER = 'Content-Type'; + /** * Data Definitions Name * @@ -60,6 +62,13 @@ class DataDefinition */ private $auth; + /** + * Content type of body + * + * @var string + */ + private $contentType; + /** * Relevant headers for the request * @@ -79,14 +88,7 @@ class DataDefinition * * @var array */ - private $metaData = []; - - /** - * Store code in api url. - * - * @var string - */ - private $storeCode; + private $operationMetadata = []; /** * Regex to check for request success. @@ -103,7 +105,7 @@ class DataDefinition private $returnRegex; /** - * DataDefinition constructor. + * OperationDefinitionObject constructor. * @param string $name * @param string $operation * @param string $dataType @@ -113,9 +115,9 @@ class DataDefinition * @param array $headers * @param array $params * @param array $metaData + * @param string $contentType * @param string $successRegex * @param string $returnRegex - * @param string $storeCode */ public function __construct( $name, @@ -127,23 +129,30 @@ public function __construct( $headers, $params, $metaData, + $contentType, $successRegex = null, - $returnRegex = null, - $storeCode = 'default' + $returnRegex = null ) { $this->name = $name; $this->operation = $operation; $this->dataType = $dataType; $this->apiMethod = $apiMethod; - $this->apiUri = $apiUri; + $this->apiUri = trim($apiUri, '/'); $this->auth = $auth; $this->headers = $headers; $this->params = $params; - $this->metaData = $metaData; - $this->storeCode = $storeCode; + $this->operationMetadata = $metaData; $this->successRegex = $successRegex; $this->returnRegex = $returnRegex; $this->apiUrl = null; + + if (!empty($contentType)) { + $this->contentType = $contentType; + } else { + $this->contentType = 'application/x-www-form-urlencoded'; + } + // add content type as a header + $this->headers[] = self::HTTP_CONTENT_TYPE_HEADER . ': ' . $this->contentType; } /** @@ -179,27 +188,20 @@ public function getApiMethod() /** * Getter for api url for a store. * - * @param string $storeCode * @return string */ - public function getApiUrl($storeCode) + public function getApiUrl() { - if (isset($storeCode)) { - $this->storeCode = $storeCode; - } - - if (strpos($this->auth, 'Formkey') === false) { - $this->apiUrl = '/rest/' . $this->storeCode . '/' . trim($this->apiUri, '/'); - } else { - $this->apiUrl = trim($this->apiUri, '/') . '/'; - } + if (!$this->apiUrl) { + $this->apiUrl = $this->apiUri; - if (array_key_exists('path', $this->params)) { - $this->addPathParam(); - } + if (array_key_exists('path', $this->params)) { + $this->addPathParam(); + } - if (array_key_exists('query', $this->params)) { - $this->addQueryParams(); + if (array_key_exists('query', $this->params)) { + $this->addQueryParams(); + } } return $this->apiUrl; @@ -226,23 +228,23 @@ public function getHeaders() } /** - * Getter for data metadata + * Getter for Content-type * - * @return array + * @return string */ - public function getMetaData() + public function getContentType() { - return $this->metaData; + return $this->contentType; } /** - * Getter for store code. + * Getter for data metadata * - * @return string + * @return array */ - public function getStoreCode() + public function getOperationMetadata() { - return $this->storeCode; + return $this->operationMetadata; } /** @@ -278,19 +280,18 @@ private function addPathParam() } /** - * Function to append query params where necessary + * Function to append or add query parameters * * @return void */ - private function addQueryParams() + public function addQueryParams() { foreach ($this->params['query'] as $paramName => $paramValue) { - if (strpos($this->apiUrl, '?') == false) { + if (strpos($this->apiUrl, '?') === false) { $this->apiUrl = $this->apiUrl . "?"; } else { $this->apiUrl = $this->apiUrl . "&"; } - $this->apiUrl = $this->apiUrl . $paramName . "=" . $paramValue; } } diff --git a/src/Magento/FunctionalTestingFramework/DataGenerator/Objects/DataElement.php b/src/Magento/FunctionalTestingFramework/DataGenerator/Objects/OperationElement.php similarity index 96% rename from src/Magento/FunctionalTestingFramework/DataGenerator/Objects/DataElement.php rename to src/Magento/FunctionalTestingFramework/DataGenerator/Objects/OperationElement.php index afa066780..0580018fd 100644 --- a/src/Magento/FunctionalTestingFramework/DataGenerator/Objects/DataElement.php +++ b/src/Magento/FunctionalTestingFramework/DataGenerator/Objects/OperationElement.php @@ -6,7 +6,7 @@ namespace Magento\FunctionalTestingFramework\DataGenerator\Objects; -class DataElement +class OperationElement { /** * Data parameter name @@ -50,7 +50,7 @@ class DataElement private $required; /** - * DataElement constructor. + * OperationElement constructor. * @param string $key * @param string $value * @param string $type @@ -118,7 +118,7 @@ public function getRequired() * @param string $type * @return array */ - public function getNestedDataElement($type) + public function getNestedOperationElement($type) { if (array_key_exists($type, $this->nestedElements)) { return $this->nestedElements[$type]; diff --git a/src/Magento/FunctionalTestingFramework/DataGenerator/Parsers/DataProfileSchemaParser.php b/src/Magento/FunctionalTestingFramework/DataGenerator/Parsers/DataProfileSchemaParser.php index c9ac3dda2..0a40afe09 100644 --- a/src/Magento/FunctionalTestingFramework/DataGenerator/Parsers/DataProfileSchemaParser.php +++ b/src/Magento/FunctionalTestingFramework/DataGenerator/Parsers/DataProfileSchemaParser.php @@ -13,6 +13,13 @@ */ class DataProfileSchemaParser { + /** + * Data Profiles. + * + * @var DataInterface + */ + private $dataProfiles; + /** * DataProfileSchemaParser constructor. * @param DataInterface $dataProfiles diff --git a/src/Magento/FunctionalTestingFramework/DataGenerator/Parsers/OperationMetadataParser.php b/src/Magento/FunctionalTestingFramework/DataGenerator/Parsers/OperationDefinitionParser.php similarity index 80% rename from src/Magento/FunctionalTestingFramework/DataGenerator/Parsers/OperationMetadataParser.php rename to src/Magento/FunctionalTestingFramework/DataGenerator/Parsers/OperationDefinitionParser.php index b3c6cc514..e7a68435c 100644 --- a/src/Magento/FunctionalTestingFramework/DataGenerator/Parsers/OperationMetadataParser.php +++ b/src/Magento/FunctionalTestingFramework/DataGenerator/Parsers/OperationDefinitionParser.php @@ -9,10 +9,17 @@ use Magento\FunctionalTestingFramework\Config\DataInterface; /** - * Class OperationMetadataParser + * Class OperationDefinitionParser */ -class OperationMetadataParser +class OperationDefinitionParser { + /** + * Meta Data. + * + * @var DataInterface + */ + private $metadata; + /** * MetadataParser constructor. * @param DataInterface $metadata diff --git a/src/Magento/FunctionalTestingFramework/DataGenerator/Persist/Curl/AdminExecutor.php b/src/Magento/FunctionalTestingFramework/DataGenerator/Persist/Curl/AdminExecutor.php index 7a56c06ec..2fd97eeb3 100644 --- a/src/Magento/FunctionalTestingFramework/DataGenerator/Persist/Curl/AdminExecutor.php +++ b/src/Magento/FunctionalTestingFramework/DataGenerator/Persist/Curl/AdminExecutor.php @@ -101,24 +101,24 @@ private function setFormKey() * Send request to the remote server. * * @param string $url - * @param array $params + * @param array $data * @param string $method * @param mixed $headers * @return void * @throws TestFrameworkException */ - public function write($url, $params = [], $method = CurlInterface::POST, $headers = []) + public function write($url, $data = [], $method = CurlInterface::POST, $headers = []) { $apiUrl = self::$adminUrl . $url; if ($this->formKey) { - $params['form_key'] = $this->formKey; + $data['form_key'] = $this->formKey; } else { throw new TestFrameworkException( sprintf('Form key is absent! Url: "%s" Response: "%s"', $url, $this->response) ); } - $this->transport->write($apiUrl, str_replace('null', '', http_build_query($params)), $method, $headers); + $this->transport->write($apiUrl, str_replace('null', '', http_build_query($data)), $method, $headers); } /** @@ -134,7 +134,7 @@ public function read($successRegex = null, $returnRegex = null) $this->response = $this->transport->read(); $this->setFormKey(); - if (isset($successRegex)) { + if (!empty($successRegex)) { preg_match( '/' . preg_quote($successRegex, '/') . '/', $this->response, @@ -145,7 +145,7 @@ public function read($successRegex = null, $returnRegex = null) } } - if (isset($returnRegex)) { + if (!empty($returnRegex)) { preg_match( '/' . preg_quote($returnRegex, '/') . '/', $this->response, diff --git a/src/Magento/FunctionalTestingFramework/DataGenerator/Persist/Curl/WebapiExecutor.php b/src/Magento/FunctionalTestingFramework/DataGenerator/Persist/Curl/WebapiExecutor.php index f5414598b..66321bf88 100644 --- a/src/Magento/FunctionalTestingFramework/DataGenerator/Persist/Curl/WebapiExecutor.php +++ b/src/Magento/FunctionalTestingFramework/DataGenerator/Persist/Curl/WebapiExecutor.php @@ -51,7 +51,8 @@ class WebapiExecutor extends AbstractExecutor implements CurlInterface private $storeCode; /** - * @construct + * WebapiExecutor Constructor. + * * @param string $storeCode */ public function __construct($storeCode = 'default') @@ -88,16 +89,16 @@ private function authorize() * Send request to the remote server. * * @param string $url - * @param array $params + * @param array $data * @param string $method * @param array $headers * @return void */ - public function write($url, $params = [], $method = CurlInterface::POST, $headers = []) + public function write($url, $data = [], $method = CurlInterface::POST, $headers = []) { $this->transport->write( - parent::$baseUrl . ltrim($url, '/'), - json_encode($params, JSON_PRETTY_PRINT), + parent::$baseUrl . 'rest/' . $this->storeCode . '/' . trim($url, '/'), + json_encode($data, JSON_PRETTY_PRINT), $method, array_unique(array_merge($headers, $this->headers)) ); @@ -108,7 +109,7 @@ public function write($url, $params = [], $method = CurlInterface::POST, $header * * @param string $successRegex * @param string $returnRegex - * @return string + * @return mixed */ public function read($successRegex = null, $returnRegex = null) { diff --git a/src/Magento/FunctionalTestingFramework/DataGenerator/Persist/CurlHandler.php b/src/Magento/FunctionalTestingFramework/DataGenerator/Persist/CurlHandler.php index 2a0166774..e7d73676e 100644 --- a/src/Magento/FunctionalTestingFramework/DataGenerator/Persist/CurlHandler.php +++ b/src/Magento/FunctionalTestingFramework/DataGenerator/Persist/CurlHandler.php @@ -7,12 +7,10 @@ use Magento\FunctionalTestingFramework\DataGenerator\Persist\Curl\AdminExecutor; use Magento\FunctionalTestingFramework\DataGenerator\Persist\Curl\WebapiExecutor; -use Magento\FunctionalTestingFramework\DataGenerator\Handlers\DataObjectHandler; -use Magento\FunctionalTestingFramework\DataGenerator\Handlers\DataDefinitionObjectHandler; +use Magento\FunctionalTestingFramework\DataGenerator\Handlers\OperationDefinitionObjectHandler; use Magento\FunctionalTestingFramework\DataGenerator\Objects\EntityDataObject; -use Magento\FunctionalTestingFramework\DataGenerator\Objects\DataDefinition; -use Magento\FunctionalTestingFramework\DataGenerator\Objects\DataElement; -use Magento\FunctionalTestingFramework\DataGenerator\Util\DataObjectExtractor; +use Magento\FunctionalTestingFramework\DataGenerator\Objects\OperationDefinitionObject; +use Magento\FunctionalTestingFramework\Exceptions\TestFrameworkException; use Magento\FunctionalTestingFramework\Util\Protocol\CurlInterface; /** @@ -20,9 +18,6 @@ */ class CurlHandler { - 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') * @@ -40,39 +35,30 @@ class CurlHandler /** * The data definitions used to map the operation. * - * @var DataDefinition $dataDefinition - */ - private $dataDefinition; - - /** - * The array of dependentEntities this class can be given. When finding linked entities, CurlHandler - * uses this repository before looking for static data. - * - * @var array + * @var OperationDefinitionObject $operationDefinition */ - private $dependentEntities = []; + private $operationDefinition; /** - * Persisted data array. + * The request data. * * @var array */ - private $persistedDataArray; + private $requestData; /** - * The array of entity name and number of objects being created, - * we don't need to track objects in update and delete operations. + * Store code in web api rest url. * - * @var array + * @var string */ - private static $entitySequences = []; + private $storeCode; /** - * Store code in web api rest url. + * If the content type is Json. * - * @var string + * @var bool */ - private $storeCode; + private $isJson; /** * Operation to Curl method mapping. @@ -86,326 +72,125 @@ class CurlHandler 'get' => CurlInterface::GET, ]; - /** - * If it's a web api request. - * - * @var bool - */ - private $isWebApiRequest; - /** * ApiSubObject constructor. * @param string $operation * @param EntityDataObject $entityObject - * @param array $dependentEntities * @param string $storeCode */ - public function __construct($operation, $entityObject, $dependentEntities = null, $storeCode = 'default') + public function __construct($operation, $entityObject, $storeCode = 'default') { $this->operation = $operation; $this->entityObject = $entityObject; $this->storeCode = $storeCode; - $this->isWebApiRequest = true; - if ($dependentEntities != null) { - foreach ($dependentEntities as $entity) { - $this->dependentEntities[$entity->getName()] = $entity; - } - } - $this->dataDefinition = DataDefinitionObjectHandler::getInstance()->getDataDefinition( + $this->operationDefinition = OperationDefinitionObjectHandler::getInstance()->getOperationDefinition( $this->operation, $this->entityObject->getType() ); + $this->isJson = false; } /** * Executes an api request based on parameters given by constructor. * - * @return string | null + * @param array $dependentEntities + * @return array | null + * @throws TestFrameworkException */ - public function executeRequest() + public function executeRequest($dependentEntities) { - $apiUrl = $this->dataDefinition->getApiUrl($this->storeCode); - - $matchedParams = []; - preg_match_all("/[{](.+?)[}]/", $apiUrl, $matchedParams); - - if (!empty($matchedParams)) { - foreach ($matchedParams[0] as $paramKey => $paramValue) { - $param = $this->entityObject->getDataByName( - $matchedParams[1][$paramKey], - EntityDataObject::CEST_UNIQUE_VALUE - ); - $apiUrl = str_replace($paramValue, $param, $apiUrl); - } - } - $executor = null; $successRegex = null; $returnRegex = null; - $headers = $this->dataDefinition->getHeaders(); - $this->persistedDataArray = $this->convertDataArray($this->entityObject, $this->dataDefinition->getMetaData()); - $authorization = $this->dataDefinition->getAuth(); - switch ($authorization) { - case 'adminOauth': - $executor = new WebapiExecutor($this->storeCode); - $executor->write( - $apiUrl, - $this->persistedDataArray, - self::$curlMethodMapping[$this->operation], - $headers - ); - break; - case 'adminFormkey': - $this->isWebApiRequest = false; - $executor = new AdminExecutor(); - $executor->write( - $apiUrl, - $this->persistedDataArray, - self::$curlMethodMapping[$this->operation], - $headers - ); - $successRegex = $this->dataDefinition->getSuccessRegex(); - $returnRegex = $this->dataDefinition->getReturnRegex(); - break; - case 'customFromkey': - $this->isWebApiRequest = false; - // TODO: add frontend request executor. - break; - } - $response = $executor->read($successRegex, $returnRegex); - $executor->close(); - return $response; - } - - /** - * If it's a web api request. - * - * @return bool - */ - public function isWebApiRequest() - { - return $this->isWebApiRequest; - } - - /** - * Get persisted data array. - * - * @return array - */ - public function getPersistedDataArray() - { - return $this->persistedDataArray; - } - - /** - * This function returns an array which is structurally equal to the data which is needed by the magento web api, - * magento backend / frontend requests for entity creation. The function retrieves an array describing the entity's - * metadata and traverses any dependencies recursively forming an array which represents the data structure for the - * request of the desired entity type. - * - * @param EntityDataObject $entityObject - * @param array $dataArrayMetadata - * @return array - * @throws \Exception - */ - public function convertDataArray($entityObject, $dataArrayMetadata) - { - $dataArray = []; - self::incrementSequence($entityObject->getName()); - - foreach ($dataArrayMetadata as $dataElement) { - if ($dataElement->getType() == DataObjectExtractor::MATA_DATA_OBJECT_NAME) { - $entityObj = $this->resolveDataObjectAndEntityData($entityObject, $dataElement->getValue()); - $dataArray[$dataElement->getValue()] = - $this->convertDataArray($entityObject, $dataElement->getNestedMetadata()); - continue; - } - - $dataElementType = $dataElement->getValue(); - - if (in_array($dataElementType, CurlHandler::PRIMITIVE_TYPES)) { - $elementData = $entityObject->getDataByName( - $dataElement->getKey(), - EntityDataObject::CEST_UNIQUE_VALUE - ); - - // 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($dataElement->getKey(), $entityObject->getUniquenessData())) { - $uniqueData = $entityObject->getUniquenessDataByName($dataElement->getKey()); - if ($uniqueData === 'suffix') { - $elementData .= (string)self::getSequence($entityObject->getName()); - } else { - $elementData = (string)self::getSequence($entityObject->getName()) . $elementData; - } - } - - $dataArray[$dataElement->getKey()] = $this->castValue($dataElementType, $elementData); - } elseif ($dataElement->getRequired()) { - throw new \Exception(sprintf( - CurlHandler::EXCEPTION_REQUIRED_DATA, - $dataElement->getType(), - $dataElement->getKey(), - $this->entityObject->getName() - )); - } - } else { - $entityNamesOfType = $entityObject->getLinkedEntitiesOfType($dataElementType); - - // If an element is required by metadata, but was not provided in the entity, throw an exception - if ($dataElement->getRequired() && $entityNamesOfType == null) { - throw new \Exception(sprintf( - CurlHandler::EXCEPTION_REQUIRED_DATA, - $dataElement->getType(), - $dataElement->getKey(), - $this->entityObject->getName() - )); - } - foreach ($entityNamesOfType as $entityName) { - $dataSubArray = $this->resolveNonPrimitiveElement($entityName, $dataElement); - - if ($dataElement->getType() == 'array') { - $dataArray[$dataElement->getKey()][] = $dataSubArray; - } else { - $dataArray[$dataElement->getKey()] = $dataSubArray; - } - } - } - } - - return $dataArray; - } + $apiUrl = $this->resolveUrlReference($this->operationDefinition->getApiUrl()); + $headers = $this->operationDefinition->getHeaders(); + $authorization = $this->operationDefinition->getAuth(); + $contentType = $this->operationDefinition->getContentType(); + $successRegex = $this->operationDefinition->getSuccessRegex(); + $returnRegex = $this->operationDefinition->getReturnRegex(); + + $operationDataResolver = new OperationDataArrayResolver($dependentEntities); + $this->requestData = $operationDataResolver->resolveOperationDataArray( + $this->entityObject, + $this->operationDefinition->getOperationMetadata(), + $this->operationDefinition->getOperation() + ); - /** - * This function does a comparison of the entity object being matched to the data 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 $dataElementValue - * @return EntityDataObject|null - */ - private function resolveDataObjectAndEntityData($entityObject, $dataElementValue) - { - if ($dataElementValue != $entityObject->getType()) { - // if we have a mismatch attempt to retrieve linked data and return just the first linkage - $linkName = $entityObject->getLinkedEntitiesOfType($dataElementValue)[0]; - return DataObjectHandler::getInstance()->getObject($linkName); + if (($contentType === 'application/json') && ($authorization === 'adminOauth')) { + $this->isJson = true; + $executor = new WebapiExecutor($this->storeCode); + } elseif ($authorization === 'adminFormKey') { + $executor = new AdminExecutor(); + //TODO: add frontend request executor + //} elseif ($authorization === 'customFromKey') { } - return $entityObject; - } - - /** - * Resolves dataObjects and pre-defined metadata (in other operation.xml file) referenced by the metadata. - * - * @param string $entityName - * @param DataElement $dataElement - * @return array - */ - private function resolveNonPrimitiveElement($entityName, $dataElement) - { - $linkedEntityObj = $this->resolveLinkedEntityObject($entityName); - - // in array case - if (!empty($dataElement->getNestedDataElement($dataElement->getValue())) - && $dataElement->getType() == 'array' - ) { - $dataSubArray = $this->convertDataArray( - $linkedEntityObj, - [$dataElement->getNestedDataElement($dataElement->getValue())] + if (!$executor) { + throw new TestFrameworkException( + sprintf( + "Invalid content type and/or auth type. content type = %s, auth type = %s\n", + $contentType, + $authorization + ) ); - - return $dataSubArray[$dataElement->getValue()]; } - $metaData = DataDefinitionObjectHandler::getInstance()->getDataDefinition( - $this->operation, - $linkedEntityObj->getType() - )->getMetaData(); - - return $this->convertDataArray($linkedEntityObj, $metaData); - } + $executor->write( + $apiUrl, + $this->requestData, + self::$curlMethodMapping[$this->operation], + $headers + ); - /** - * Method to wrap entity resolution, checks locally defined dependent entities first - * - * @param string $entityName - * @return EntityDataObject - */ - private function resolveLinkedEntityObject($entityName) - { - // check our dependent entity list to see if we have this defined - if (array_key_exists($entityName, $this->dependentEntities)) { - return $this->dependentEntities[$entityName]; - } + $response = $executor->read($successRegex, $returnRegex); + $executor->close(); - return DataObjectHandler::getInstance()->getObject($entityName); + return $response; } /** - * Increment an entity's sequence number by 1. + * Getter for request data in array. * - * @param string $entityName - * @return void + * @return array */ - private static function incrementSequence($entityName) + public function getRequestDataArray() { - if (array_key_exists($entityName, self::$entitySequences)) { - self::$entitySequences[$entityName]++; - } else { - self::$entitySequences[$entityName] = 1; - } + return $this->requestData; } /** - * Get the current sequence number for an entity. + * If content type of a request is Json. * - * @param string $entityName - * @return int + * @return bool */ - private static function getSequence($entityName) + public function isContentTypeJson() { - if (array_key_exists($entityName, self::$entitySequences)) { - return self::$entitySequences[$entityName]; - } - return 0; + return $this->isJson; } - // @codingStandardsIgnoreStart /** - * This function takes a string value and its corresponding type and returns the string cast - * into its the type passed. + * Resolve rul reference from entity data. * - * @param string $type - * @param string $value - * @return mixed + * @param string $urlIn + * @return string */ - private function castValue($type, $value) + private function resolveUrlReference($urlIn) { - $newVal = $value; + $urlOut = $urlIn; + $matchedParams = []; + preg_match_all("/[{](.+?)[}]/", $urlIn, $matchedParams); - switch ($type) { - case 'string': - break; - case 'integer': - $newVal = (integer)$value; - break; - case 'boolean': - if (strtolower($newVal) === 'false') { - return false; - } - $newVal = (boolean)$value; - break; - case 'double': - $newVal = (double)$value; - break; + if (!empty($matchedParams)) { + foreach ($matchedParams[0] as $paramKey => $paramValue) { + $param = $this->entityObject->getDataByName( + $matchedParams[1][$paramKey], + EntityDataObject::CEST_UNIQUE_VALUE + ); + $urlOut = str_replace($paramValue, $param, $urlIn); + } } - - return $newVal; + return $urlOut; } - // @codingStandardsIgnoreEnd } diff --git a/src/Magento/FunctionalTestingFramework/DataGenerator/Persist/DataPersistHandler.php b/src/Magento/FunctionalTestingFramework/DataGenerator/Persist/DataPersistenceHandler.php similarity index 57% rename from src/Magento/FunctionalTestingFramework/DataGenerator/Persist/DataPersistHandler.php rename to src/Magento/FunctionalTestingFramework/DataGenerator/Persist/DataPersistenceHandler.php index 1b4b5ced2..bc8d0a477 100644 --- a/src/Magento/FunctionalTestingFramework/DataGenerator/Persist/DataPersistHandler.php +++ b/src/Magento/FunctionalTestingFramework/DataGenerator/Persist/DataPersistenceHandler.php @@ -9,9 +9,9 @@ use Magento\FunctionalTestingFramework\DataGenerator\Objects\EntityDataObject; /** - * Class DataPersistHandler + * Class DataPersistenceHandler */ -class DataPersistHandler +class DataPersistenceHandler { /** * Entity object data to use for create, delete, or update. @@ -41,7 +41,7 @@ class DataPersistHandler private $storeCode; /** - * DataPersistHandler constructor. + * DataPersistenceHandler constructor. * * @param EntityDataObject $entityObject * @param array $dependentObjects @@ -61,25 +61,15 @@ public function __construct($entityObject, $dependentObjects = null) */ public function createEntity($storeCode = null) { - if (isset($storeCode)) { + if (!empty($storeCode)) { $this->storeCode = $storeCode; } - $curlHandler = new CurlHandler('create', $this->entityObject, $this->dependentObjects, $this->storeCode); - $result = $curlHandler->executeRequest(); - - $persistedDataArray = $curlHandler->getPersistedDataArray(); - if ($curlHandler->isWebApiRequest()) { - $persistedDataArray = array_merge($persistedDataArray, json_decode($result, true)); - } else { - $persistedDataArray = array_merge($persistedDataArray, ['return' => $result]); - } - - $this->createdObject = new EntityDataObject( - $this->entityObject->getName(), - $this->entityObject->getType(), - $persistedDataArray, - null, - null + $curlHandler = new CurlHandler('create', $this->entityObject, $this->storeCode); + $result = $curlHandler->executeRequest($this->dependentObjects); + $this->setCreatedEntity( + $result, + $curlHandler->getRequestDataArray(), + $curlHandler->isContentTypeJson() ); } @@ -87,17 +77,15 @@ public function createEntity($storeCode = null) * Function which executes a delete request based on specific operation metadata * * @param string $storeCode - * @return string | false + * @return void */ public function deleteEntity($storeCode = null) { - if (isset($storeCode)) { + if (!empty($storeCode)) { $this->storeCode = $storeCode; } - $curlHandler = new CurlHandler('delete', $this->createdObject, null, $this->storeCode); - $result = $curlHandler->executeRequest(); - - return $result; + $curlHandler = new CurlHandler('delete', $this->createdObject, $this->storeCode); + $result = $curlHandler->executeRequest($this->dependentObjects); } /** @@ -124,4 +112,59 @@ public function getCreatedDataByName($dataName) { }*/ + + /** + * Save created entity. + * + * @param mixed $response + * @param array $requestDataArray + * @param bool $isJson + * @return void + */ + private function setCreatedEntity($response, $requestDataArray, $isJson) + { + if ($isJson) { + $persistedData = array_merge($requestDataArray, json_decode($response, true)); + } else { + $persistedData = array_merge( + $this->convertToFlatArray($requestDataArray), + ['return' => $response] + ); + } + + $this->createdObject = new EntityDataObject( + $this->entityObject->getName(), + $this->entityObject->getType(), + $persistedData, + null, + null + ); + } + + /** + * Convert an multi-dimensional array to flat array. + * + * @param array $arrayIn + * @param string $rootKey + * @return array + */ + private function convertToFlatArray($arrayIn, $rootKey = '') + { + $arrayOut = []; + foreach ($arrayIn as $key => $value) { + if (is_array($value)) { + if (!empty($rootKey)) { + $newRootKey = $rootKey . '[' . $key . ']'; + } else { + $newRootKey = $key; + } + $arrayOut = array_merge($arrayOut, $this->convertToFlatArray($value, $newRootKey)); + } elseif (!empty($rootKey)) { + $arrayOut[$rootKey . '[' . $key . ']'] = $value; + } else { + $arrayOut[$key] = $value; + } + } + return $arrayOut; + } } diff --git a/src/Magento/FunctionalTestingFramework/DataGenerator/Persist/OperationDataArrayResolver.php b/src/Magento/FunctionalTestingFramework/DataGenerator/Persist/OperationDataArrayResolver.php new file mode 100644 index 000000000..f175b5271 --- /dev/null +++ b/src/Magento/FunctionalTestingFramework/DataGenerator/Persist/OperationDataArrayResolver.php @@ -0,0 +1,277 @@ +dependentEntities[$entity->getName()] = $entity; + } + } + } + + /** + * This function returns an array which is structurally equal to the data which is needed by the magento web api, + * magento backend / frontend requests for entity creation. The function retrieves an array describing the entity's + * operation metadata and traverses any dependencies recursively forming an array which represents the data + * structure for the request of the desired entity type. + * + * @param EntityDataObject $entityObject + * @param array $operationMetadata + * @param string $operation + * @return array + * @throws \Exception + */ + public function resolveOperationDataArray($entityObject, $operationMetadata, $operation) + { + $operationDataArray = []; + self::incrementSequence($entityObject->getName()); + + foreach ($operationMetadata as $operationElement) { + if ($operationElement->getType() == OperationElementExtractor::OPERATION_OBJECT_OBJ_NAME) { + $entityObj = $this->resolveOperationObjectAndEntityData($entityObject, $operationElement->getValue()); + $operationDataArray[$operationElement->getValue()] = + $this->resolveOperationDataArray($entityObj, $operationElement->getNestedMetadata(), $operation); + continue; + } + + $operationElementType = $operationElement->getValue(); + + if (in_array($operationElementType, self::PRIMITIVE_TYPES)) { + $elementData = $entityObject->getDataByName( + $operationElement->getKey(), + EntityDataObject::CEST_UNIQUE_VALUE + ); + + // If data was defined at all, attempt to put it into operation data array + // If data was not defined, and element is required, throw exception + // If no data is defined, don't input defaults per primitive into operation data array + if ($elementData != null) { + if (array_key_exists($operationElement->getKey(), $entityObject->getUniquenessData())) { + $uniqueData = $entityObject->getUniquenessDataByName($operationElement->getKey()); + if ($uniqueData === 'suffix') { + $elementData .= (string)self::getSequence($entityObject->getName()); + } else { + $elementData = (string)self::getSequence($entityObject->getName()) . $elementData; + } + } + $operationDataArray[$operationElement->getKey()] = $this->castValue( + $operationElementType, + $elementData + ); + + } elseif ($operationElement->getRequired()) { + throw new \Exception(sprintf( + self::EXCEPTION_REQUIRED_DATA, + $operationElement->getType(), + $operationElement->getKey(), + $entityObject->getName() + )); + } + } else { + $entityNamesOfType = $entityObject->getLinkedEntitiesOfType($operationElementType); + + // If an element is required by metadata, but was not provided in the entity, throw an exception + if ($operationElement->getRequired() && $entityNamesOfType == null) { + throw new \Exception(sprintf( + self::EXCEPTION_REQUIRED_DATA, + $operationElement->getType(), + $operationElement->getKey(), + $entityObject->getName() + )); + } + foreach ($entityNamesOfType as $entityName) { + $operationDataSubArray = $this->resolveNonPrimitiveElement( + $entityName, + $operationElement, + $operation + ); + + if ($operationElement->getType() == OperationDefinitionObjectHandler::ENTITY_OPERATION_ARRAY) { + $operationDataArray[$operationElement->getKey()][] = $operationDataSubArray; + } else { + $operationDataArray[$operationElement->getKey()] = $operationDataSubArray; + } + } + } + } + + return $operationDataArray; + } + + /** + * This function does a comparison of the entity object being matched to the operation 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 $operationElementValue + * @return EntityDataObject|null + */ + private function resolveOperationObjectAndEntityData($entityObject, $operationElementValue) + { + if ($operationElementValue != $entityObject->getType()) { + // if we have a mismatch attempt to retrieve linked data and return just the first linkage + $linkName = $entityObject->getLinkedEntitiesOfType($operationElementValue)[0]; + return DataObjectHandler::getInstance()->getObject($linkName); + } + + return $entityObject; + } + + /** + * Resolves DataObjects and pre-defined metadata (in other operation.xml file) referenced by the operation + * + * @param string $entityName + * @param OperationElement $operationElement + * @param string $operation + * @return array + */ + private function resolveNonPrimitiveElement($entityName, $operationElement, $operation) + { + $linkedEntityObj = $this->resolveLinkedEntityObject($entityName); + + // in array case + if (!empty($operationElement->getNestedOperationElement($operationElement->getValue())) + && $operationElement->getType() == OperationDefinitionObjectHandler::ENTITY_OPERATION_ARRAY + ) { + $operationSubArray = $this->resolveOperationDataArray( + $linkedEntityObj, + [$operationElement->getNestedOperationElement($operationElement->getValue())], + $operation + ); + + return $operationSubArray[$operationElement->getValue()]; + } + + $operationMetadata = OperationDefinitionObjectHandler::getInstance()->getOperationDefinition( + $operation, + $linkedEntityObj->getType() + )->getOperationMetadata(); + + return $this->resolveOperationDataArray($linkedEntityObj, $operationMetadata, $operation); + } + + /** + * Method to wrap entity resolution, checks locally defined dependent entities first + * + * @param string $entityName + * @return EntityDataObject + */ + private function resolveLinkedEntityObject($entityName) + { + // check our dependent entity list to see if we have this defined + if (array_key_exists($entityName, $this->dependentEntities)) { + return $this->dependentEntities[$entityName]; + } + + return DataObjectHandler::getInstance()->getObject($entityName); + } + + /** + * Increment an entity's sequence number by 1. + * + * @param string $entityName + * @return void + */ + private static function incrementSequence($entityName) + { + if (array_key_exists($entityName, self::$entitySequences)) { + self::$entitySequences[$entityName]++; + } else { + self::$entitySequences[$entityName] = 1; + } + } + + /** + * Get the current sequence number for an entity. + * + * @param string $entityName + * @return int + */ + private static function getSequence($entityName) + { + if (array_key_exists($entityName, self::$entitySequences)) { + return self::$entitySequences[$entityName]; + } + return 0; + } + + // @codingStandardsIgnoreStart + /** + * This function takes a string value and its corresponding type and returns the string cast + * into its the type passed. + * + * @param string $type + * @param string $value + * @return mixed + */ + private function castValue($type, $value) + { + $newVal = $value; + + switch ($type) { + case 'string': + break; + case 'integer': + $newVal = (integer)$value; + break; + case 'boolean': + if (strtolower($newVal) === 'false') { + return false; + } + $newVal = (boolean)$value; + break; + case 'double': + $newVal = (double)$value; + break; + } + + return $newVal; + } + // @codingStandardsIgnoreEnd +} diff --git a/src/Magento/FunctionalTestingFramework/DataGenerator/Util/DataObjectExtractor.php b/src/Magento/FunctionalTestingFramework/DataGenerator/Util/DataObjectExtractor.php deleted file mode 100644 index 52aa04da9..000000000 --- a/src/Magento/FunctionalTestingFramework/DataGenerator/Util/DataObjectExtractor.php +++ /dev/null @@ -1,132 +0,0 @@ -extractDataEntries($metaData, $dataObjectArray[DataObjectExtractor::DATA_OBJECT_ENTRY]); - } - - // extract nested arrays - if (array_key_exists(DataObjectExtractor::DATA_OBJECT_ARRAY, $dataObjectArray)) { - $this->extractDataArrays($metaData, $dataObjectArray[DataObjectExtractor::DATA_OBJECT_ARRAY]); - } - - // extract nested - if (array_key_exists(DataObjectExtractor::MATA_DATA_OBJECT_NAME, $dataObjectArray)) { - foreach ($dataObjectArray[DataObjectExtractor::MATA_DATA_OBJECT_NAME] as $dataObject) { - $nestedDataElement = $this->extractDataObject($dataObject); - $metaData[] = $nestedDataElement; - } - } - - // a dataObject specified in xml must contain corresponding metadata for the object - if (empty($metaData)) { - throw new \Exception("must specify dataObject metadata if declaration is used"); - } - - return new DataElement( - $dataDefKey, - $dataType, - DataObjectExtractor::MATA_DATA_OBJECT_NAME, - $dataObjectArray[DataDefinitionObjectHandler::ENTITY_OPERATION_REQUIRED] ?? null, - $nestedDataElements, - $metaData - ); - } - - /** - * Creates and Adds relevant DataElements from data entries defined within dataObject array - * - * @param array &$metaData - * @param array $dataEntryArray - * @return void - */ - private function extractDataEntries(&$metaData, $dataEntryArray) - { - foreach ($dataEntryArray as $dataEntryType) { - $metaData[] = new DataElement( - $dataEntryType[DataDefinitionObjectHandler::ENTITY_OPERATION_ENTRY_KEY], - $dataEntryType[DataDefinitionObjectHandler::ENTITY_OPERATION_ENTRY_VALUE], - DataDefinitionObjectHandler::ENTITY_OPERATION_ENTRY, - $dataEntryType[DataDefinitionObjectHandler::ENTITY_OPERATION_REQUIRED] ?? null - ); - } - } - - /** - * Creates and Adds relevant DataElements from data arrays defined within dataObject array - * - * @param array &$dataArrayData - * @param array $dataArrayArray - * @return void - */ - private function extractDataArrays(&$dataArrayData, $dataArrayArray) - { - foreach ($dataArrayArray as $dataEntryType) { - $dataElementValue = - $dataEntryType[DataDefinitionObjectHandler::ENTITY_OPERATION_ARRAY_VALUE][0] - [DataObjectExtractor::DATA_OBJECT_ARRAY_VALUE] ?? null; - - $nestedDataElements = []; - if (array_key_exists(DataObjectExtractor::MATA_DATA_OBJECT_NAME, $dataEntryType)) { - //add the key to reference this object later - $dataObjectKeyedArray = $dataEntryType[DataObjectExtractor::MATA_DATA_OBJECT_NAME][0]; - $dataObjectKeyedArray[DataObjectExtractor::DATA_OBJECT_KEY] = - $dataEntryType[DataDefinitionObjectHandler::ENTITY_OPERATION_ARRAY_KEY]; - $dataElement = $this->extractDataObject($dataObjectKeyedArray); - $dataElementValue = $dataElement->getValue(); - $nestedDataElements[$dataElement->getValue()] = $dataElement; - } - $dataArrayData[] = new DataElement( - $dataEntryType[DataDefinitionObjectHandler::ENTITY_OPERATION_ARRAY_KEY], - $dataElementValue, - DataDefinitionObjectHandler::ENTITY_OPERATION_ARRAY, - $dataEntryType[DataDefinitionObjectHandler::ENTITY_OPERATION_REQUIRED] ?? null, - $nestedDataElements - ); - } - } -} diff --git a/src/Magento/FunctionalTestingFramework/DataGenerator/Util/OperationElementExtractor.php b/src/Magento/FunctionalTestingFramework/DataGenerator/Util/OperationElementExtractor.php new file mode 100644 index 000000000..6f2e3e447 --- /dev/null +++ b/src/Magento/FunctionalTestingFramework/DataGenerator/Util/OperationElementExtractor.php @@ -0,0 +1,139 @@ +extractOperationField( + $operationElements, + $operationElementArray[OperationElementExtractor::OPERATION_OBJECT_ENTRY] + ); + } + + // extract nested arrays + if (array_key_exists(OperationElementExtractor::OPERATION_OBJECT_ARRAY, $operationElementArray)) { + $this->extractOperationArray( + $operationElements, + $operationElementArray[OperationElementExtractor::OPERATION_OBJECT_ARRAY] + ); + } + + // extract nested + if (array_key_exists(OperationElementExtractor::OPERATION_OBJECT_OBJ_NAME, $operationElementArray)) { + foreach ($operationElementArray[OperationElementExtractor::OPERATION_OBJECT_OBJ_NAME] as $operationObject) { + $nestedOperationElement = $this->extractOperationElement($operationObject); + $operationElements[] = $nestedOperationElement; + } + } + + // a dataObject specified in xml must contain corresponding metadata for the object + if (empty($operationElements)) { + throw new \Exception("must specify dataObject metadata if declaration is used"); + } + + return new OperationElement( + $operationDefKey, + $dataType, + OperationElementExtractor::OPERATION_OBJECT_OBJ_NAME, + $operationElementArray[OperationDefinitionObjectHandler::ENTITY_OPERATION_REQUIRED] ?? null, + $nestedOperationElements, + $operationElements + ); + } + + /** + * Creates and Adds relevant DataElements from data entries defined within dataObject array + * + * @param array &$operationElements + * @param array $operationFieldArray + * @return void + */ + private function extractOperationField(&$operationElements, $operationFieldArray) + { + foreach ($operationFieldArray as $operationFieldType) { + $operationElements[] = new OperationElement( + $operationFieldType[OperationDefinitionObjectHandler::ENTITY_OPERATION_ENTRY_KEY], + $operationFieldType[OperationDefinitionObjectHandler::ENTITY_OPERATION_ENTRY_VALUE], + OperationDefinitionObjectHandler::ENTITY_OPERATION_ENTRY, + $operationFieldType[OperationDefinitionObjectHandler::ENTITY_OPERATION_REQUIRED] ?? null + ); + } + } + + /** + * Creates and Adds relevant DataElements from data arrays defined within dataObject array + * + * @param array &$operationArrayData + * @param array $operationArrayArray + * @return void + */ + private function extractOperationArray(&$operationArrayData, $operationArrayArray) + { + foreach ($operationArrayArray as $operationFieldType) { + $operationElementValue = + $operationFieldType[OperationDefinitionObjectHandler::ENTITY_OPERATION_ARRAY_VALUE][0] + [OperationElementExtractor::OPERATION_OBJECT_ARRAY_VALUE] ?? null; + + $nestedOperationElements = []; + if (array_key_exists(OperationElementExtractor::OPERATION_OBJECT_OBJ_NAME, $operationFieldType)) { + //add the key to reference this object later + $operationObjectKeyedArray = $operationFieldType + [OperationElementExtractor::OPERATION_OBJECT_OBJ_NAME][0]; + $operationObjectKeyedArray[OperationElementExtractor::OPERATION_OBJECT_KEY] = + $operationFieldType[OperationDefinitionObjectHandler::ENTITY_OPERATION_ARRAY_KEY]; + $operationElement = $this->extractOperationElement($operationObjectKeyedArray); + $operationElementValue = $operationElement->getValue(); + $nestedOperationElements[$operationElement->getValue()] = $operationElement; + } + $operationArrayData[] = new OperationElement( + $operationFieldType[OperationDefinitionObjectHandler::ENTITY_OPERATION_ARRAY_KEY], + $operationElementValue, + OperationDefinitionObjectHandler::ENTITY_OPERATION_ARRAY, + $operationFieldType[OperationDefinitionObjectHandler::ENTITY_OPERATION_REQUIRED] ?? null, + $nestedOperationElements + ); + } + } +} diff --git a/src/Magento/FunctionalTestingFramework/DataGenerator/etc/dataOperation.xsd b/src/Magento/FunctionalTestingFramework/DataGenerator/etc/dataOperation.xsd index 3587efdb1..eadc0e396 100644 --- a/src/Magento/FunctionalTestingFramework/DataGenerator/etc/dataOperation.xsd +++ b/src/Magento/FunctionalTestingFramework/DataGenerator/etc/dataOperation.xsd @@ -6,11 +6,12 @@ - - + + + @@ -18,7 +19,6 @@ - @@ -26,11 +26,11 @@ - + - + - + @@ -47,7 +47,7 @@ - + @@ -79,9 +79,8 @@ - - - + + \ No newline at end of file diff --git a/src/Magento/FunctionalTestingFramework/Util/Protocol/CurlInterface.php b/src/Magento/FunctionalTestingFramework/Util/Protocol/CurlInterface.php index 990f341d6..8e868d44d 100644 --- a/src/Magento/FunctionalTestingFramework/Util/Protocol/CurlInterface.php +++ b/src/Magento/FunctionalTestingFramework/Util/Protocol/CurlInterface.php @@ -32,19 +32,19 @@ public function addOption($option, $value); * Send request to the remote server. * * @param string $url - * @param mixed $params + * @param mixed $body * @param string $method * @param mixed $headers * @return void */ - public function write($url, $params = [], $method = CurlInterface::POST, $headers = []); + public function write($url, $body = [], $method = CurlInterface::POST, $headers = []); /** * Read response from server. * * @param string $successRegex * @param string $returnRegex - * @return string|array + * @return mixed */ public function read($successRegex = null, $returnRegex = null); diff --git a/src/Magento/FunctionalTestingFramework/Util/Protocol/CurlTransport.php b/src/Magento/FunctionalTestingFramework/Util/Protocol/CurlTransport.php index 8d826a331..38afcc5ab 100644 --- a/src/Magento/FunctionalTestingFramework/Util/Protocol/CurlTransport.php +++ b/src/Magento/FunctionalTestingFramework/Util/Protocol/CurlTransport.php @@ -118,13 +118,13 @@ public function setConfig(array $config = []) * Send request to the remote server. * * @param string $url - * @param mixed $params + * @param mixed $body * @param string $method * @param mixed $headers * @return void * @throws TestFrameworkException */ - public function write($url, $params = [], $method = CurlInterface::POST, $headers = []) + public function write($url, $body = [], $method = CurlInterface::POST, $headers = []) { $this->applyConfig(); $options = [ @@ -138,11 +138,11 @@ public function write($url, $params = [], $method = CurlInterface::POST, $header switch ($method) { case CurlInterface::POST: $options[CURLOPT_POST] = true; - $options[CURLOPT_POSTFIELDS] = $params; + $options[CURLOPT_POSTFIELDS] = $body; break; case CurlInterface::PUT: $options[CURLOPT_CUSTOMREQUEST] = self::PUT; - $options[CURLOPT_POSTFIELDS] = $params; + $options[CURLOPT_POSTFIELDS] = $body; break; case CurlInterface::DELETE: $options[CURLOPT_CUSTOMREQUEST] = self::DELETE; diff --git a/src/Magento/FunctionalTestingFramework/Util/TestGenerator.php b/src/Magento/FunctionalTestingFramework/Util/TestGenerator.php index ce38c1cb3..3af4008b2 100644 --- a/src/Magento/FunctionalTestingFramework/Util/TestGenerator.php +++ b/src/Magento/FunctionalTestingFramework/Util/TestGenerator.php @@ -212,7 +212,7 @@ private function generateUseStatementsPhp() $useStatementsPhp = "use Magento\FunctionalTestingFramework\AcceptanceTester;\n"; $useStatementsPhp .= "use Magento\FunctionalTestingFramework\DataGenerator\Handlers\DataObjectHandler;\n"; - $useStatementsPhp .= "use Magento\FunctionalTestingFramework\DataGenerator\Persist\DataPersistHandler;\n"; + $useStatementsPhp .= "use Magento\FunctionalTestingFramework\DataGenerator\Persist\DataPersistenceHandler;\n"; $useStatementsPhp .= "use Magento\FunctionalTestingFramework\DataGenerator\Objects\EntityDataObject;\n"; $allureStatements = [ @@ -500,15 +500,15 @@ private function generateStepsPhp($stepsObject, $stepsData, $hookObject = false) if ($hookObject) { $createEntityFunctionCall = sprintf("\t\t\$this->%s->createEntity(", $key); - $dataPersistHandlerFunctionCall = sprintf( - "\t\t\$this->%s = new DataPersistHandler($%s", + $dataPersistenceHandlerFunctionCall = sprintf( + "\t\t\$this->%s = new DataPersistenceHandler($%s", $key, $entity ); } else { $createEntityFunctionCall = sprintf("\t\t\$%s->createEntity(", $key); - $dataPersistHandlerFunctionCall = sprintf( - "\t\t$%s = new DataPersistHandler($%s", + $dataPersistenceHandlerFunctionCall = sprintf( + "\t\t$%s = new DataPersistenceHandler($%s", $key, $entity ); @@ -521,7 +521,7 @@ private function generateStepsPhp($stepsObject, $stepsData, $hookObject = false) } //If required-entities are defined, reassign dataObject to not overwrite the static definition. - //Also, DataPersistHandler needs to be defined with customData array. + //Also, DataPersistenceHandler needs to be defined with customData array. if (!empty($requiredEntities)) { $testSteps .= sprintf( "\t\t$%s = new EntityDataObject($%s->getName(), $%s->getType(), $%s->getData() @@ -535,14 +535,14 @@ private function generateStepsPhp($stepsObject, $stepsData, $hookObject = false) $entity ); - $dataPersistHandlerFunctionCall .= sprintf( + $dataPersistenceHandlerFunctionCall .= sprintf( ", [%s]);\n", implode(', ', $requiredEntityObjects) ); } else { - $dataPersistHandlerFunctionCall .= ");\n"; + $dataPersistenceHandlerFunctionCall .= ");\n"; } - $testSteps .= $dataPersistHandlerFunctionCall; + $testSteps .= $dataPersistenceHandlerFunctionCall; $testSteps .= $createEntityFunctionCall; break; case "deleteData": @@ -874,7 +874,7 @@ private function generateHooksPhp($hookObjects) foreach ($hookObject->getActions() as $step) { if ($step->getType() == "createData") { $hooks .= "\t/**\n"; - $hooks .= sprintf("\t * @var DataPersistHandler $%s;\n", $step->getMergeKey()); + $hooks .= sprintf("\t * @var DataPersistenceHandler $%s;\n", $step->getMergeKey()); $hooks .= "\t */\n"; $hooks .= sprintf("\tprotected $%s;\n\n", $step->getMergeKey()); $createData = true; From f9dd74a44b0f30a6631658f9bd17d861738c3661 Mon Sep 17 00:00:00 2001 From: Kevin Kozan Date: Fri, 29 Sep 2017 09:38:06 -0500 Subject: [PATCH 11/19] MQE-392: Generated code is incorrect if there is a space between parameters - Added trim to parameter processing to prevent erroneous spaces. --- .../FunctionalTestingFramework/Test/Objects/ActionObject.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Magento/FunctionalTestingFramework/Test/Objects/ActionObject.php b/src/Magento/FunctionalTestingFramework/Test/Objects/ActionObject.php index 767012694..cc473075e 100644 --- a/src/Magento/FunctionalTestingFramework/Test/Objects/ActionObject.php +++ b/src/Magento/FunctionalTestingFramework/Test/Objects/ActionObject.php @@ -392,11 +392,12 @@ private function matchParameterReferences($reference, $parameters) ); } - //Attempt to Resolve {{data}} references to actual output. + //Attempt to Resolve {{data}} references to actual output. Trim parameter for whitespace before processing it. //If regex matched it means that it's either a 'StringLiteral' or $key.data$/$$key.data$$ reference. //Else assume it's a normal {{data.key}} reference and recurse through findAndReplace $resolvedParameters = []; foreach ($parameters as $parameter) { + $parameter = trim($parameter); preg_match_all("/[$'][\w.$]+[$']/", $parameter, $match); if (!empty($match[0])) { $resolvedParameters[] = ltrim(rtrim($parameter, "'"), "'"); From 9010ab1d356b2e5333a2c08cb4f3772a0adb14ea Mon Sep 17 00:00:00 2001 From: Kevin Kozan Date: Fri, 29 Sep 2017 09:43:36 -0500 Subject: [PATCH 12/19] MQE-393: [XML Parser] waitForLoadingMaskToDisppear usage causes parser Error - Added waitForLoadingMaskToDisappear to di.xml --- etc/di.xml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/etc/di.xml b/etc/di.xml index 081b324fc..6cb672c81 100644 --- a/etc/di.xml +++ b/etc/di.xml @@ -226,7 +226,7 @@ key name name - mergeKey + mergeKey *Cest.xml Cest @@ -236,9 +236,9 @@ - mergeKey - mergeKey - mergeKey + mergeKey + mergeKey + mergeKey name name key From ad8cad9882d051f36685442d977e0c4c4eab3b17 Mon Sep 17 00:00:00 2001 From: Ian Meron Date: Mon, 2 Oct 2017 12:55:41 -0500 Subject: [PATCH 13/19] MQE-309:[Customizability] Update nested API dependency schema and execution - change test schema for simpler persisted entity relationships - change data persistence handler to resolve entity relationships - change data schema to allow var declaration as a placehold for persisted data --- etc/di.xml | 16 +--- .../Handlers/DataObjectHandler.php | 18 ++++- .../OperationDefinitionObjectHandler.php | 4 +- .../Objects/EntityDataObject.php | 28 ++++++- .../Persist/DataPersistenceHandler.php | 9 ++- .../Persist/OperationDataArrayResolver.php | 78 ++++++++++++++++++- .../DataGenerator/etc/dataProfileSchema.xsd | 10 +++ .../Test/etc/testSchema.xsd | 2 +- .../Util/TestGenerator.php | 56 +++---------- 9 files changed, 152 insertions(+), 69 deletions(-) diff --git a/etc/di.xml b/etc/di.xml index 40a10eb3f..81f36ceb0 100644 --- a/etc/di.xml +++ b/etc/di.xml @@ -204,10 +204,8 @@ name name - key - key name - name + persistedKey mergeKey *Cest.xml @@ -223,15 +221,9 @@ mergeKey name name - key - key - name - key - key - name - key - key - name + persistedKey + persistedKey + persistedKey name diff --git a/src/Magento/FunctionalTestingFramework/DataGenerator/Handlers/DataObjectHandler.php b/src/Magento/FunctionalTestingFramework/DataGenerator/Handlers/DataObjectHandler.php index 633028538..f52659b34 100644 --- a/src/Magento/FunctionalTestingFramework/DataGenerator/Handlers/DataObjectHandler.php +++ b/src/Magento/FunctionalTestingFramework/DataGenerator/Handlers/DataObjectHandler.php @@ -43,6 +43,12 @@ class DataObjectHandler implements ObjectHandlerInterface const ARRAY_ELEMENT_ITEM = 'item'; const ARRAY_ELEMENT_ITEM_VALUE = 'value'; + const VAR_VALUES = 'var'; + const VAR_KEY = 'key'; + const VAR_ENTITY = 'entityType'; + const VAR_FIELD = 'entityKey'; + const VAR_ENTITY_FIELD_SEPARATOR = '->'; + const REQUIRED_ENTITY = 'required-entity'; const REQUIRED_ENTITY_TYPE = 'type'; const REQUIRED_ENTITY_VALUE = 'value'; @@ -150,6 +156,7 @@ private function parseDataEntities() $dataValues = []; $linkedEntities = []; $arrayValues = []; + $vars = []; $uniquenessValues = []; if (array_key_exists(self::DATA_VALUES, $entity)) { @@ -186,12 +193,21 @@ private function parseDataEntities() } } + if (array_key_exists(self::VAR_VALUES, $entity)) { + foreach ($entity[self::VAR_VALUES] as $varElement) { + $varKey = $varElement[self::VAR_KEY]; + $varValue = $varElement[self::VAR_ENTITY] . self::VAR_ENTITY_FIELD_SEPARATOR . $varElement[self::VAR_FIELD]; + $vars[$varKey] = $varValue; + } + } + $entityDataObject = new EntityDataObject( $entityName, $entityType, $dataValues, $linkedEntities, - $uniquenessValues + $uniquenessValues, + $vars ); $this->data[$entityDataObject->getName()] = $entityDataObject; diff --git a/src/Magento/FunctionalTestingFramework/DataGenerator/Handlers/OperationDefinitionObjectHandler.php b/src/Magento/FunctionalTestingFramework/DataGenerator/Handlers/OperationDefinitionObjectHandler.php index 4d28caab0..9dda26324 100644 --- a/src/Magento/FunctionalTestingFramework/DataGenerator/Handlers/OperationDefinitionObjectHandler.php +++ b/src/Magento/FunctionalTestingFramework/DataGenerator/Handlers/OperationDefinitionObjectHandler.php @@ -201,14 +201,14 @@ private function initDataDefinitions() } else { $value = $operationField[OperationDefinitionObjectHandler::ENTITY_OPERATION_ARRAY_VALUE][0] [OperationDefinitionObjectHandler::ENTITY_OPERATION_ARRAY_VALUE]; - $type = [OperationDefinitionObjectHandler::ENTITY_OPERATION_ARRAY_VALUE]; + $type = OperationDefinitionObjectHandler::ENTITY_OPERATION_ARRAY; } $operationElements[] = new OperationElement( $operationField[OperationDefinitionObjectHandler::ENTITY_OPERATION_ARRAY_KEY], $value, $type, - $dataEntryType[OperationDefinitionObjectHandler::ENTITY_OPERATION_REQUIRED] ?? null, + $operationField[OperationDefinitionObjectHandler::ENTITY_OPERATION_REQUIRED] ?? null, $subOperationElements ); } diff --git a/src/Magento/FunctionalTestingFramework/DataGenerator/Objects/EntityDataObject.php b/src/Magento/FunctionalTestingFramework/DataGenerator/Objects/EntityDataObject.php index db602ad07..41730f3aa 100644 --- a/src/Magento/FunctionalTestingFramework/DataGenerator/Objects/EntityDataObject.php +++ b/src/Magento/FunctionalTestingFramework/DataGenerator/Objects/EntityDataObject.php @@ -34,6 +34,13 @@ class EntityDataObject */ private $linkedEntities = []; + /** + * An array of variable mappings for static data + * + * @var array + */ + private $vars; + /** * An array of Data Name to Data Value * @@ -63,8 +70,9 @@ class EntityDataObject * @param array $data * @param array $linkedEntities * @param array $uniquenessData + * @param array $vars */ - public function __construct($entityName, $entityType, $data, $linkedEntities, $uniquenessData) + public function __construct($entityName, $entityType, $data, $linkedEntities, $uniquenessData, $vars = []) { $this->name = $entityName; $this->type = $entityType; @@ -73,6 +81,8 @@ public function __construct($entityName, $entityType, $data, $linkedEntities, $u if ($uniquenessData) { $this->uniquenessData = $uniquenessData; } + + $this->vars = $vars; } /** @@ -189,6 +199,22 @@ public function getDataByName($dataName, $uniDataFormat) return null; } + /** + * Function which returns a reference to another entity (e.g. a var with entity="category" field="id" returns as + * category->id) + * + * @param string $dataKey + * @return array|null + */ + public function getVarReference($dataKey) + { + if (array_key_exists($dataKey, $this->vars)) { + return $this->vars[$dataKey]; + } + + return null; + } + /** * This function takes an array of entityTypes indexed by name and a string that represents the type of interest. * The function returns an array of entityNames relevant to the specified type. diff --git a/src/Magento/FunctionalTestingFramework/DataGenerator/Persist/DataPersistenceHandler.php b/src/Magento/FunctionalTestingFramework/DataGenerator/Persist/DataPersistenceHandler.php index bc8d0a477..b44382aed 100644 --- a/src/Magento/FunctionalTestingFramework/DataGenerator/Persist/DataPersistenceHandler.php +++ b/src/Magento/FunctionalTestingFramework/DataGenerator/Persist/DataPersistenceHandler.php @@ -31,7 +31,7 @@ class DataPersistenceHandler * Array of dependent entities, handed to CurlHandler when entity is created. * @var array|null */ - private $dependentObjects = []; + private $dependentObjects; /** * Store code in web api rest url. @@ -46,11 +46,14 @@ class DataPersistenceHandler * @param EntityDataObject $entityObject * @param array $dependentObjects */ - public function __construct($entityObject, $dependentObjects = null) + public function __construct($entityObject, $dependentObjects = []) { $this->entityObject = clone $entityObject; - $this->dependentObjects = $dependentObjects; $this->storeCode = 'default'; + + foreach ($dependentObjects as $dependentObject) { + $this->dependentObjects[] = $dependentObject->getCreatedObject(); + } } /** diff --git a/src/Magento/FunctionalTestingFramework/DataGenerator/Persist/OperationDataArrayResolver.php b/src/Magento/FunctionalTestingFramework/DataGenerator/Persist/OperationDataArrayResolver.php index f175b5271..49cb535a8 100644 --- a/src/Magento/FunctionalTestingFramework/DataGenerator/Persist/OperationDataArrayResolver.php +++ b/src/Magento/FunctionalTestingFramework/DataGenerator/Persist/OperationDataArrayResolver.php @@ -18,8 +18,7 @@ class OperationDataArrayResolver 'string', 'boolean', 'integer', - 'double', - OperationDefinitionObjectHandler::ENTITY_OPERATION_ARRAY + 'double' ]; const EXCEPTION_REQUIRED_DATA = "%s of key \" %s\" in \"%s\" is required by metadata, but was not provided."; @@ -82,9 +81,10 @@ public function resolveOperationDataArray($entityObject, $operationMetadata, $op $operationElementType = $operationElement->getValue(); if (in_array($operationElementType, self::PRIMITIVE_TYPES)) { - $elementData = $entityObject->getDataByName( + $elementData = $this->resolvePrimitiveReference( + $entityObject, $operationElement->getKey(), - EntityDataObject::CEST_UNIQUE_VALUE + $operationElement->getType() ); // If data was defined at all, attempt to put it into operation data array @@ -143,6 +143,67 @@ public function resolveOperationDataArray($entityObject, $operationMetadata, $op return $operationDataArray; } + /** + * Resolves a reference for a primitive piece of data, if the data cannot be found as a defined field, the method + * looks to see if any vars have been declared with the same operationKey and resolves based on defined dependent + * entities. + * + * @param EntityDataObject $entityObject + * @param string $operationKey + * @param string $operationElementType + * @return array|string + */ + private function resolvePrimitiveReference($entityObject, $operationKey, $operationElementType) + { + $elementData = $entityObject->getDataByName( + $operationKey, + EntityDataObject::CEST_UNIQUE_VALUE + ); + + if ($elementData == null && $entityObject->getVarReference($operationKey) != null) { + list($type, $field) = explode( + DataObjectHandler::VAR_ENTITY_FIELD_SEPARATOR, + $entityObject->getVarReference($operationKey) + ); + + if ($operationElementType == OperationDefinitionObjectHandler::ENTITY_OPERATION_ARRAY) { + $elementDatas = []; + $entities = $this->getDependentEntitiesOfType($type); + foreach ($entities as $entity) { + $elementDatas[] = $entity->getDataByName($field, EntityDataObject::CEST_UNIQUE_VALUE); + } + + return $elementDatas; + + } + + $entity = $this->getDependentEntitiesOfType($type)[0]; + $elementData = $entity->getDataByName($field, EntityDataObject::CEST_UNIQUE_VALUE); + } + + return $elementData; + } + + /** + * Returns all dependent entities of the type passed in as an arg (the dependent entities are given at runtime, + * and are not statically defined). + * + * @param string $type + * @return array + */ + private function getDependentEntitiesOfType($type) + { + $entitiesOfType = []; + + foreach ($this->dependentEntities as $dependentEntity) { + if ($dependentEntity->getType() == $type) { + $entitiesOfType[] = $dependentEntity; + } + } + + return $entitiesOfType; + } + /** * This function does a comparison of the entity object being matched to the operation element. If there is a * mismatch in type we attempt to use a nested entity, if the entities are properly matched, we simply return @@ -254,6 +315,15 @@ private function castValue($type, $value) { $newVal = $value; + if (is_array($value)) { + $newVals = []; + foreach($value as $val) { + $newVals[] = $this->castValue($type, $val); + } + + return $newVals; + } + switch ($type) { case 'string': break; diff --git a/src/Magento/FunctionalTestingFramework/DataGenerator/etc/dataProfileSchema.xsd b/src/Magento/FunctionalTestingFramework/DataGenerator/etc/dataProfileSchema.xsd index 24a7eed6b..c0494c911 100644 --- a/src/Magento/FunctionalTestingFramework/DataGenerator/etc/dataProfileSchema.xsd +++ b/src/Magento/FunctionalTestingFramework/DataGenerator/etc/dataProfileSchema.xsd @@ -30,6 +30,16 @@ + + + + + + + + + + diff --git a/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd b/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd index c0677a132..b64c6d839 100644 --- a/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd +++ b/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd @@ -380,7 +380,7 @@ - + diff --git a/src/Magento/FunctionalTestingFramework/Util/TestGenerator.php b/src/Magento/FunctionalTestingFramework/Util/TestGenerator.php index 3af4008b2..2f197e8b5 100644 --- a/src/Magento/FunctionalTestingFramework/Util/TestGenerator.php +++ b/src/Magento/FunctionalTestingFramework/Util/TestGenerator.php @@ -18,6 +18,8 @@ class TestGenerator { + const REQUIRED_ENTITY_REFERENCE = 'persistedKey'; + /** * Path to the export dir. * @@ -487,13 +489,16 @@ private function generateStepsPhp($stepsObject, $stepsData, $hookObject = false) foreach ($customActionAttributes as $customAttribute) { if (is_array($customAttribute) && $customAttribute['nodeName'] = 'required-entity') { if ($hookObject) { - $requiredEntities [] = "\$this->" . $customAttribute['name'] . "->getName() => " . - "\$this->" . $customAttribute['name'] . "->getType()"; - $requiredEntityObjects [] = '$this->' . $customAttribute['name']; + $requiredEntities [] = "\$this->" . $customAttribute[self::REQUIRED_ENTITY_REFERENCE] . + "->getName() => " . "\$this->" . $customAttribute[self::REQUIRED_ENTITY_REFERENCE] . + "->getType()"; + $requiredEntityObjects [] = '$this->' . $customAttribute + [self::REQUIRED_ENTITY_REFERENCE]; } else { - $requiredEntities [] = "\$" . $customAttribute['name'] . "->getName() => " - . "\$" . $customAttribute['name'] . "->getType()"; - $requiredEntityObjects [] = '$' . $customAttribute['name']; + $requiredEntities [] = "\$" . $customAttribute[self::REQUIRED_ENTITY_REFERENCE] + . "->getName() => " . "\$" . $customAttribute[self::REQUIRED_ENTITY_REFERENCE] . + "->getType()"; + $requiredEntityObjects [] = '$' . $customAttribute[self::REQUIRED_ENTITY_REFERENCE]; } } } @@ -523,18 +528,6 @@ private function generateStepsPhp($stepsObject, $stepsData, $hookObject = false) //If required-entities are defined, reassign dataObject to not overwrite the static definition. //Also, DataPersistenceHandler needs to be defined with customData array. if (!empty($requiredEntities)) { - $testSteps .= sprintf( - "\t\t$%s = new EntityDataObject($%s->getName(), $%s->getType(), $%s->getData() - , array_merge($%s->getLinkedEntities(), [%s]), $%s->getUniquenessData());\n", - $entity, - $entity, - $entity, - $entity, - $entity, - implode(", ", $requiredEntities), - $entity - ); - $dataPersistenceHandlerFunctionCall .= sprintf( ", [%s]);\n", implode(', ', $requiredEntityObjects) @@ -560,33 +553,6 @@ private function generateStepsPhp($stepsObject, $stepsData, $hookObject = false) $testSteps .= sprintf("\t\t$%s->deleteEntity();\n", $key); } break; - case "entity": - $entityData = '['; - foreach ($stepsData[$customActionAttributes['name']] as $dataKey => $dataValue) { - $variableReplace = $this->resolveTestVariable($dataValue, true); - $entityData .= sprintf("\"%s\" => \"%s\", ", $dataKey, $variableReplace); - } - $entityData .= ']'; - if ($hookObject) { - // no uniqueness attributes for data allowed within entity defined in cest. - $testSteps .= sprintf( - "\t\t\$this->%s = new EntityDataObject(\"%s\",\"%s\",%s,null,null);\n", - $customActionAttributes['name'], - $customActionAttributes['name'], - $customActionAttributes['type'], - $entityData - ); - } else { - // no uniqueness attributes for data allowed within entity defined in cest. - $testSteps .= sprintf( - "\t\t$%s = new EntityDataObject(\"%s\",\"%s\",%s,null,null);\n", - $customActionAttributes['name'], - $customActionAttributes['name'], - $customActionAttributes['type'], - $entityData - ); - } - break; case "dontSeeCurrentUrlEquals": case "dontSeeCurrentUrlMatches": case "seeInPopup": From cef0e3abf43ab52513c620f3b68df014bae16b96 Mon Sep 17 00:00:00 2001 From: Ian Meron Date: Mon, 2 Oct 2017 13:35:13 -0500 Subject: [PATCH 14/19] MQE-309:[Customizability] Update nested API dependency schema and execution - remove old entity test schema declarations --- .../Test/etc/testSchema.xsd | 33 ------------------- 1 file changed, 33 deletions(-) diff --git a/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd b/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd index b64c6d839..edabbcbea 100644 --- a/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd +++ b/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd @@ -105,7 +105,6 @@ - @@ -618,38 +617,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - From 8b873a8baa0aa1ecf748ded3097035462a985d2a Mon Sep 17 00:00:00 2001 From: Kevin Kozan Date: Tue, 3 Oct 2017 13:44:23 -0500 Subject: [PATCH 15/19] MQE-391: Variable is not correctly resolved when array symbol ([ or ]) is inside $$ signs - Updated Regex for persisted variable replacement. --- src/Magento/FunctionalTestingFramework/Util/TestGenerator.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Magento/FunctionalTestingFramework/Util/TestGenerator.php b/src/Magento/FunctionalTestingFramework/Util/TestGenerator.php index 2f197e8b5..2780df25a 100644 --- a/src/Magento/FunctionalTestingFramework/Util/TestGenerator.php +++ b/src/Magento/FunctionalTestingFramework/Util/TestGenerator.php @@ -772,7 +772,7 @@ private function resolveTestVariable($inputString, $quoteBreak = false) $replaced = false; // Check for Cest-scope variables first, stricter regex match. - preg_match_all("/\\$\\$[\w.]+\\$\\$/", $outputString, $matches); + preg_match_all("/\\$\\$[\w.\[\]]+\\$\\$/", $outputString, $matches); foreach ($matches[0] as $match) { $replacement = null; $variable = $this->stripAndSplitReference($match, '$$'); @@ -791,7 +791,7 @@ private function resolveTestVariable($inputString, $quoteBreak = false) } // Check Test-scope variables - preg_match_all("/\\$[\w.]+\\$/", $outputString, $matches); + preg_match_all("/\\$[\w.\[\]]+\\$/", $outputString, $matches); foreach ($matches[0] as $match) { $replacement = null; $variable = $this->stripAndSplitReference($match, '$'); From 72dab137e83b98283615699db27a5d476a19de4b Mon Sep 17 00:00:00 2001 From: Kevin Kozan Date: Tue, 3 Oct 2017 13:49:06 -0500 Subject: [PATCH 16/19] MQE-414: [XML Parser] PageObject/SectionObject picking up SampleTemplates - Edited di.xml to use Module instead of Mask for Pages/Sections. --- etc/di.xml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/etc/di.xml b/etc/di.xml index 81f36ceb0..de716b7eb 100644 --- a/etc/di.xml +++ b/etc/di.xml @@ -74,27 +74,27 @@ - Magento\FunctionalTestingFramework\Config\FileResolver\Mask + Magento\FunctionalTestingFramework\Config\FileResolver\Module Magento\FunctionalTestingFramework\Config\Converter Magento\FunctionalTestingFramework\Config\SchemaLocator\Page name name - #\.xml$# + *Page.xml Page - Magento\FunctionalTestingFramework\Config\FileResolver\Mask + Magento\FunctionalTestingFramework\Config\FileResolver\Module Magento\FunctionalTestingFramework\Config\Converter Magento\FunctionalTestingFramework\Config\SchemaLocator\Section name name - #\.xml$# + *Section.xml Section From 3ba4a6d599780b37ff2511662c0b3c316deb76dd Mon Sep 17 00:00:00 2001 From: Kevin Kozan Date: Wed, 4 Oct 2017 13:00:15 -0500 Subject: [PATCH 17/19] MQE-410: [Generator] Bad output with Parameterized Persisted Unique data - Changed selector resolution to call uniquenessFunctionCall. Solves issue with safe backwards compatibility. - Generator was stripping ' from selector strings like: #'{{name}}', turning that into #{{name}} when resolved. --- .../FunctionalTestingFramework/Util/TestGenerator.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Magento/FunctionalTestingFramework/Util/TestGenerator.php b/src/Magento/FunctionalTestingFramework/Util/TestGenerator.php index 2780df25a..971688fcf 100644 --- a/src/Magento/FunctionalTestingFramework/Util/TestGenerator.php +++ b/src/Magento/FunctionalTestingFramework/Util/TestGenerator.php @@ -397,7 +397,7 @@ private function generateStepsPhp($stepsObject, $stepsData, $hookObject = false) if (isset($customActionAttributes['selectorArray'])) { $selector = $customActionAttributes['selectorArray']; } elseif (isset($customActionAttributes['selector'])) { - $selector = $this->wrapWithDoubleQuotes($customActionAttributes['selector']); + $selector = $this->addUniquenessFunctionCall($customActionAttributes['selector']); } if (isset($customActionAttributes['selector1'])) { @@ -1050,7 +1050,7 @@ private function wrapWithDoubleQuotes($input) } /** - * Strip beginning and ending quotes of input string. + * Strip beginning and ending double quotes of input string. * * @param string $input * @return string @@ -1060,10 +1060,10 @@ private function stripWrappedQuotes($input) if (empty($input)) { return ''; } - if (substr($input, 0, 1) === '"' || substr($input, 0, 1) === "'") { + if (substr($input, 0, 1) === '"') { $input = substr($input, 1); } - if (substr($input, -1, 1) === '"' || substr($input, -1, 1) === "'") { + if (substr($input, -1, 1) === '"') { $input = substr($input, 0, -1); } return $input; From e12f672cee6a4b5ab8e08d03a6a906323fef1e8d Mon Sep 17 00:00:00 2001 From: Alex Kolesnyk Date: Thu, 5 Oct 2017 15:45:02 +0300 Subject: [PATCH 18/19] MQE-424: Fix static code issues in MFTF tests and MFTF --- .../DataGenerator/Persist/Curl/AdminExecutor.php | 4 ++-- .../DataGenerator/Persist/Curl/WebapiExecutor.php | 4 ++-- .../DataGenerator/Persist/DataPersistenceHandler.php | 2 +- .../Util/Protocol/CurlInterface.php | 8 ++++---- .../Util/Protocol/CurlTransport.php | 8 ++++---- 5 files changed, 13 insertions(+), 13 deletions(-) diff --git a/src/Magento/FunctionalTestingFramework/DataGenerator/Persist/Curl/AdminExecutor.php b/src/Magento/FunctionalTestingFramework/DataGenerator/Persist/Curl/AdminExecutor.php index 2fd97eeb3..5e90cf7d8 100644 --- a/src/Magento/FunctionalTestingFramework/DataGenerator/Persist/Curl/AdminExecutor.php +++ b/src/Magento/FunctionalTestingFramework/DataGenerator/Persist/Curl/AdminExecutor.php @@ -103,7 +103,7 @@ private function setFormKey() * @param string $url * @param array $data * @param string $method - * @param mixed $headers + * @param array $headers * @return void * @throws TestFrameworkException */ @@ -162,7 +162,7 @@ public function read($successRegex = null, $returnRegex = null) * Add additional option to cURL. * * @param int $option the CURLOPT_* constants - * @param mixed $value + * @param int|string|bool|array $value * @return void */ public function addOption($option, $value) diff --git a/src/Magento/FunctionalTestingFramework/DataGenerator/Persist/Curl/WebapiExecutor.php b/src/Magento/FunctionalTestingFramework/DataGenerator/Persist/Curl/WebapiExecutor.php index 66321bf88..652f620b4 100644 --- a/src/Magento/FunctionalTestingFramework/DataGenerator/Persist/Curl/WebapiExecutor.php +++ b/src/Magento/FunctionalTestingFramework/DataGenerator/Persist/Curl/WebapiExecutor.php @@ -109,7 +109,7 @@ public function write($url, $data = [], $method = CurlInterface::POST, $headers * * @param string $successRegex * @param string $returnRegex - * @return mixed + * @return string */ public function read($successRegex = null, $returnRegex = null) { @@ -121,7 +121,7 @@ public function read($successRegex = null, $returnRegex = null) * Add additional option to cURL. * * @param int $option the CURLOPT_* constants - * @param mixed $value + * @param int|string|bool|array $value * @return void */ public function addOption($option, $value) diff --git a/src/Magento/FunctionalTestingFramework/DataGenerator/Persist/DataPersistenceHandler.php b/src/Magento/FunctionalTestingFramework/DataGenerator/Persist/DataPersistenceHandler.php index b44382aed..7c80765b7 100644 --- a/src/Magento/FunctionalTestingFramework/DataGenerator/Persist/DataPersistenceHandler.php +++ b/src/Magento/FunctionalTestingFramework/DataGenerator/Persist/DataPersistenceHandler.php @@ -119,7 +119,7 @@ public function getCreatedDataByName($dataName) /** * Save created entity. * - * @param mixed $response + * @param string|array $response * @param array $requestDataArray * @param bool $isJson * @return void diff --git a/src/Magento/FunctionalTestingFramework/Util/Protocol/CurlInterface.php b/src/Magento/FunctionalTestingFramework/Util/Protocol/CurlInterface.php index 8e868d44d..792e0c6d4 100644 --- a/src/Magento/FunctionalTestingFramework/Util/Protocol/CurlInterface.php +++ b/src/Magento/FunctionalTestingFramework/Util/Protocol/CurlInterface.php @@ -23,7 +23,7 @@ interface CurlInterface * Add additional option to cURL. * * @param int $option - * @param mixed $value + * @param int|string|bool|array $value * @return $this */ public function addOption($option, $value); @@ -32,9 +32,9 @@ public function addOption($option, $value); * Send request to the remote server. * * @param string $url - * @param mixed $body + * @param array $body * @param string $method - * @param mixed $headers + * @param array $headers * @return void */ public function write($url, $body = [], $method = CurlInterface::POST, $headers = []); @@ -44,7 +44,7 @@ public function write($url, $body = [], $method = CurlInterface::POST, $headers * * @param string $successRegex * @param string $returnRegex - * @return mixed + * @return string|array */ public function read($successRegex = null, $returnRegex = null); diff --git a/src/Magento/FunctionalTestingFramework/Util/Protocol/CurlTransport.php b/src/Magento/FunctionalTestingFramework/Util/Protocol/CurlTransport.php index 38afcc5ab..5f02f476d 100644 --- a/src/Magento/FunctionalTestingFramework/Util/Protocol/CurlTransport.php +++ b/src/Magento/FunctionalTestingFramework/Util/Protocol/CurlTransport.php @@ -93,7 +93,7 @@ public function setOptions(array $options = []) * Add additional option to cURL. * * @param int $option - * @param mixed $value + * @param int|string|bool|array $value * @return $this */ public function addOption($option, $value) @@ -118,9 +118,9 @@ public function setConfig(array $config = []) * Send request to the remote server. * * @param string $url - * @param mixed $body + * @param array $body * @param string $method - * @param mixed $headers + * @param array $headers * @return void * @throws TestFrameworkException */ @@ -228,7 +228,7 @@ public function getError() * Get information regarding a specific transfer. * * @param int $opt CURLINFO option - * @return mixed + * @return string|array */ public function getInfo($opt = 0) { From a0f3637629b04be0aadf4a7dd64b03b9a0cc9219 Mon Sep 17 00:00:00 2001 From: Kevin Kozan Date: Thu, 5 Oct 2017 14:45:12 -0500 Subject: [PATCH 19/19] MQE-403: Cannot use multiple actionGroups in Test - di.xml update, turns out there's way more wrong here than just di.xml - Changed CAP to append actiongroup Mergekey to steps when processing ActionGroups, otherwise the CAP would find duplicate keys and overwrite them (since the ActionGroup steps technically had the same keys). --- etc/di.xml | 8 +++--- .../Test/Objects/ActionGroupObject.php | 28 +++++++++---------- .../Test/Util/ActionMergeUtil.php | 2 +- 3 files changed, 18 insertions(+), 20 deletions(-) diff --git a/etc/di.xml b/etc/di.xml index de716b7eb..c07bfe127 100644 --- a/etc/di.xml +++ b/etc/di.xml @@ -206,7 +206,7 @@ name name persistedKey - mergeKey + mergeKey *Cest.xml Cest @@ -216,9 +216,9 @@ - mergeKey - mergeKey - mergeKey + mergeKey + mergeKey + mergeKey name name persistedKey diff --git a/src/Magento/FunctionalTestingFramework/Test/Objects/ActionGroupObject.php b/src/Magento/FunctionalTestingFramework/Test/Objects/ActionGroupObject.php index e51117e36..a88bcae61 100644 --- a/src/Magento/FunctionalTestingFramework/Test/Objects/ActionGroupObject.php +++ b/src/Magento/FunctionalTestingFramework/Test/Objects/ActionGroupObject.php @@ -54,9 +54,10 @@ public function __construct($name, $arguments, $actions) * Gets the ordered steps including merged waits * * @param array $arguments + * @param string $actionReferenceKey * @return array */ - public function getSteps($arguments) + public function getSteps($arguments, $actionReferenceKey) { $mergeUtil = new ActionMergeUtil(); $args = $this->arguments; @@ -65,7 +66,7 @@ public function getSteps($arguments) $args = array_merge($args, $arguments); } - return $mergeUtil->resolveActionSteps($this->getResolvedActionsWithArgs($args), true); + return $mergeUtil->resolveActionSteps($this->getResolvedActionsWithArgs($args, $actionReferenceKey), true); } /** @@ -73,17 +74,18 @@ public function getSteps($arguments) * action objects with proper argument.field references. * * @param array $arguments + * @param string $actionReferenceKey * @return array */ - private function getResolvedActionsWithArgs($arguments) + private function getResolvedActionsWithArgs($arguments, $actionReferenceKey) { $resolvedActions = []; $regexPattern = '/{{([\w]+)/'; foreach ($this->parsedActions as $action) { $varAttributes = array_intersect(self::VAR_ATTRIBUTES, array_keys($action->getCustomActionAttributes())); + $newActionAttributes = []; if (!empty($varAttributes)) { - $newActionAttributes = []; // 1 check to see if we have pertinent var foreach ($varAttributes as $varAttribute) { $attributeValue = $action->getCustomActionAttributes()[$varAttribute]; @@ -98,18 +100,14 @@ private function getResolvedActionsWithArgs($arguments) $matches ); } - - $resolvedActions[$action->getMergeKey()] = new ActionObject( - $action->getMergeKey(), - $action->getType(), - array_merge($action->getCustomActionAttributes(), $newActionAttributes), - $action->getLinkedAction(), - $action->getOrderOffset() - ); - } else { - // add action here if we do not see any userInput in this particular action - $resolvedActions[$action->getMergeKey()] = $action; } + $resolvedActions[$action->getMergeKey() . $actionReferenceKey] = new ActionObject( + $action->getMergeKey() . $actionReferenceKey, + $action->getType(), + array_merge($action->getCustomActionAttributes(), $newActionAttributes), + $action->getLinkedAction(), + $action->getOrderOffset() + ); } return $resolvedActions; diff --git a/src/Magento/FunctionalTestingFramework/Test/Util/ActionMergeUtil.php b/src/Magento/FunctionalTestingFramework/Test/Util/ActionMergeUtil.php index 133ace223..ad38df07f 100644 --- a/src/Magento/FunctionalTestingFramework/Test/Util/ActionMergeUtil.php +++ b/src/Magento/FunctionalTestingFramework/Test/Util/ActionMergeUtil.php @@ -77,7 +77,7 @@ private function resolveActionGroups($mergedSteps) $mergedStep->getCustomActionAttributes()[ActionObjectExtractor::ACTION_GROUP_REF] ); $args = $mergedStep->getCustomActionAttributes()[ActionObjectExtractor::ACTION_GROUP_ARGUMENTS] ?? null; - $actionsToMerge = $actionGroup->getSteps($args); + $actionsToMerge = $actionGroup->getSteps($args, $key); $newOrderedList = $newOrderedList + $actionsToMerge; } else { $newOrderedList[$key] = $mergedStep;