From d26a1c181c18c1b278d5f77fe9df17b7b1bca9ad Mon Sep 17 00:00:00 2001 From: Grigoruta Cristian Date: Mon, 2 Apr 2018 18:54:37 +0300 Subject: [PATCH 001/111] Added the 'waitForPageLoad' action after every 'amOnPage' action --- .../Util/TestGenerator.php | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/Magento/FunctionalTestingFramework/Util/TestGenerator.php b/src/Magento/FunctionalTestingFramework/Util/TestGenerator.php index 07f069c10..446a07905 100644 --- a/src/Magento/FunctionalTestingFramework/Util/TestGenerator.php +++ b/src/Magento/FunctionalTestingFramework/Util/TestGenerator.php @@ -1866,4 +1866,18 @@ private function printRuleErrorToConsole($key, $tagName, $attributes) $message .= '" can be use for action "' . $tagName . "\".\n"; print $message; } + + /** + * Go to a page and wait for ajax requests to finish + * + * @param string $page + * @throws \Exception + */ + public function amOnPage($page) + { + $url = Uri::appendPath($this->config['url'], $page); + $this->debugSection('GET', $url); + $this->webDriver->get($url); + $this->waitForPageLoad(); + } } From c5624e2c670634977926f2c10ab2fe5ecef01bc4 Mon Sep 17 00:00:00 2001 From: Grigoruta Cristian Date: Mon, 2 Apr 2018 19:59:34 +0300 Subject: [PATCH 002/111] Cleaned up some code --- src/Magento/FunctionalTestingFramework/Util/TestGenerator.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Magento/FunctionalTestingFramework/Util/TestGenerator.php b/src/Magento/FunctionalTestingFramework/Util/TestGenerator.php index 446a07905..ea599b3ee 100644 --- a/src/Magento/FunctionalTestingFramework/Util/TestGenerator.php +++ b/src/Magento/FunctionalTestingFramework/Util/TestGenerator.php @@ -1872,6 +1872,7 @@ private function printRuleErrorToConsole($key, $tagName, $attributes) * * @param string $page * @throws \Exception + * @return void */ public function amOnPage($page) { From dfcd5c7800b09e742f0df4aeed4d57699fb21174 Mon Sep 17 00:00:00 2001 From: Tom Reece Date: Thu, 19 Apr 2018 14:50:30 -0500 Subject: [PATCH 003/111] MQE-969: Add some missing @throws phpdoc annotations --- .../DataGenerator/Handlers/DataObjectHandler.php | 2 ++ .../Handlers/OperationDefinitionObjectHandler.php | 3 +++ .../DataGenerator/Objects/EntityDataObject.php | 1 + .../DataGenerator/Persist/Curl/AdminExecutor.php | 1 + .../DataGenerator/Persist/Curl/FrontendExecutor.php | 2 ++ .../DataGenerator/Persist/Curl/WebapiExecutor.php | 5 +++++ .../DataGenerator/Persist/CurlHandler.php | 1 + .../DataGenerator/Persist/DataPersistenceHandler.php | 7 +++++++ .../Persist/OperationDataArrayResolver.php | 5 +++++ .../Page/Handlers/PageObjectHandler.php | 3 +++ .../Page/Handlers/SectionObjectHandler.php | 2 ++ .../Page/Objects/PageObject.php | 2 ++ .../Suite/Generators/GroupClassGenerator.php | 5 +++++ .../Suite/Handlers/SuiteObjectHandler.php | 3 +++ .../Suite/SuiteGenerator.php | 11 +++++++++++ .../Suite/Util/SuiteObjectExtractor.php | 4 ++++ .../Test/Handlers/TestObjectHandler.php | 3 +++ .../Test/Objects/ActionGroupObject.php | 1 + .../Test/Objects/ActionObject.php | 12 +++++++++++- .../Test/Util/ActionGroupObjectExtractor.php | 2 ++ .../Test/Util/ActionMergeUtil.php | 3 +++ .../Test/Util/ActionObjectExtractor.php | 3 +++ .../Test/Util/TestHookObjectExtractor.php | 1 + .../Test/Util/TestObjectExtractor.php | 2 +- 24 files changed, 82 insertions(+), 2 deletions(-) diff --git a/src/Magento/FunctionalTestingFramework/DataGenerator/Handlers/DataObjectHandler.php b/src/Magento/FunctionalTestingFramework/DataGenerator/Handlers/DataObjectHandler.php index 95fb6139c..0e79bcac1 100644 --- a/src/Magento/FunctionalTestingFramework/DataGenerator/Handlers/DataObjectHandler.php +++ b/src/Magento/FunctionalTestingFramework/DataGenerator/Handlers/DataObjectHandler.php @@ -8,6 +8,7 @@ use Magento\FunctionalTestingFramework\DataGenerator\Objects\EntityDataObject; use Magento\FunctionalTestingFramework\DataGenerator\Parsers\DataProfileSchemaParser; +use Magento\FunctionalTestingFramework\Exceptions\XmlException; use Magento\FunctionalTestingFramework\ObjectManager\ObjectHandlerInterface; use Magento\FunctionalTestingFramework\ObjectManagerFactory; @@ -104,6 +105,7 @@ public function getAllObjects() * * @param string[] $parserOutput primitive array output from the Magento parser * @return EntityDataObject[] + * @throws XmlException */ private function processParserOutput($parserOutput) { diff --git a/src/Magento/FunctionalTestingFramework/DataGenerator/Handlers/OperationDefinitionObjectHandler.php b/src/Magento/FunctionalTestingFramework/DataGenerator/Handlers/OperationDefinitionObjectHandler.php index c79c711ff..66ca3f594 100644 --- a/src/Magento/FunctionalTestingFramework/DataGenerator/Handlers/OperationDefinitionObjectHandler.php +++ b/src/Magento/FunctionalTestingFramework/DataGenerator/Handlers/OperationDefinitionObjectHandler.php @@ -9,6 +9,7 @@ use Magento\FunctionalTestingFramework\DataGenerator\Objects\OperationElement; use Magento\FunctionalTestingFramework\DataGenerator\Parsers\OperationDefinitionParser; use Magento\FunctionalTestingFramework\DataGenerator\Util\OperationElementExtractor; +use Magento\FunctionalTestingFramework\Exceptions\XmlException; use Magento\FunctionalTestingFramework\ObjectManager\ObjectHandlerInterface; use Magento\FunctionalTestingFramework\ObjectManagerFactory; @@ -125,6 +126,8 @@ public function getOperationDefinition($operation, $dataType) * into an array of objects. * * @return void + * @throws \Exception + * * @SuppressWarnings(PHPMD.CyclomaticComplexity) * @SuppressWarnings(PHPMD.NPathComplexity) * @SuppressWarnings(PHPMD.UnusedPrivateMethod) diff --git a/src/Magento/FunctionalTestingFramework/DataGenerator/Objects/EntityDataObject.php b/src/Magento/FunctionalTestingFramework/DataGenerator/Objects/EntityDataObject.php index 74cef78f2..d8564a26b 100644 --- a/src/Magento/FunctionalTestingFramework/DataGenerator/Objects/EntityDataObject.php +++ b/src/Magento/FunctionalTestingFramework/DataGenerator/Objects/EntityDataObject.php @@ -152,6 +152,7 @@ public function getDataByName($name, $uniquenessFormat) * @param string $uniqueData * @param string $uniqueDataFormat * @return null|string + * @throws TestFrameworkException */ private function formatUniqueData($name, $uniqueData, $uniqueDataFormat) { diff --git a/src/Magento/FunctionalTestingFramework/DataGenerator/Persist/Curl/AdminExecutor.php b/src/Magento/FunctionalTestingFramework/DataGenerator/Persist/Curl/AdminExecutor.php index fb30704a7..bec0e8fd3 100644 --- a/src/Magento/FunctionalTestingFramework/DataGenerator/Persist/Curl/AdminExecutor.php +++ b/src/Magento/FunctionalTestingFramework/DataGenerator/Persist/Curl/AdminExecutor.php @@ -54,6 +54,7 @@ class AdminExecutor extends AbstractExecutor implements CurlInterface * @param bool $removeBackend * * @constructor + * @throws TestFrameworkException */ public function __construct($removeBackend) { diff --git a/src/Magento/FunctionalTestingFramework/DataGenerator/Persist/Curl/FrontendExecutor.php b/src/Magento/FunctionalTestingFramework/DataGenerator/Persist/Curl/FrontendExecutor.php index b6f448dc0..4725c2c1e 100644 --- a/src/Magento/FunctionalTestingFramework/DataGenerator/Persist/Curl/FrontendExecutor.php +++ b/src/Magento/FunctionalTestingFramework/DataGenerator/Persist/Curl/FrontendExecutor.php @@ -62,6 +62,8 @@ class FrontendExecutor extends AbstractExecutor implements CurlInterface * * @param string $customerEmail * @param string $customerPassWord + * + * @throws TestFrameworkException */ public function __construct($customerEmail, $customerPassWord) { diff --git a/src/Magento/FunctionalTestingFramework/DataGenerator/Persist/Curl/WebapiExecutor.php b/src/Magento/FunctionalTestingFramework/DataGenerator/Persist/Curl/WebapiExecutor.php index 9a93cded3..4e07c0bf5 100644 --- a/src/Magento/FunctionalTestingFramework/DataGenerator/Persist/Curl/WebapiExecutor.php +++ b/src/Magento/FunctionalTestingFramework/DataGenerator/Persist/Curl/WebapiExecutor.php @@ -6,6 +6,7 @@ namespace Magento\FunctionalTestingFramework\DataGenerator\Persist\Curl; +use Magento\FunctionalTestingFramework\Exceptions\TestFrameworkException; use Magento\FunctionalTestingFramework\Util\Protocol\CurlInterface; use Magento\FunctionalTestingFramework\Util\Protocol\CurlTransport; @@ -54,6 +55,7 @@ class WebapiExecutor extends AbstractExecutor implements CurlInterface * WebapiExecutor Constructor. * * @param string $storeCode + * @throws TestFrameworkException */ public function __construct($storeCode = null) { @@ -70,6 +72,7 @@ public function __construct($storeCode = null) * Returns the authorization token needed for some requests via REST call. * * @return void + * @throws TestFrameworkException */ protected function authorize() { @@ -94,6 +97,7 @@ protected function authorize() * @param string $method * @param array $headers * @return void + * @throws TestFrameworkException */ public function write($url, $data = [], $method = CurlInterface::POST, $headers = []) { @@ -111,6 +115,7 @@ public function write($url, $data = [], $method = CurlInterface::POST, $headers * @param string $successRegex * @param string $returnRegex * @return string + * @throws TestFrameworkException */ 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 63144a9ff..437ce69d5 100644 --- a/src/Magento/FunctionalTestingFramework/DataGenerator/Persist/CurlHandler.php +++ b/src/Magento/FunctionalTestingFramework/DataGenerator/Persist/CurlHandler.php @@ -100,6 +100,7 @@ public function __construct($operation, $entityObject, $storeCode = null) * @param array $dependentEntities * @return array | null * @throws TestFrameworkException + * @throws \Exception */ public function executeRequest($dependentEntities) { diff --git a/src/Magento/FunctionalTestingFramework/DataGenerator/Persist/DataPersistenceHandler.php b/src/Magento/FunctionalTestingFramework/DataGenerator/Persist/DataPersistenceHandler.php index 290a14c4e..e19151980 100644 --- a/src/Magento/FunctionalTestingFramework/DataGenerator/Persist/DataPersistenceHandler.php +++ b/src/Magento/FunctionalTestingFramework/DataGenerator/Persist/DataPersistenceHandler.php @@ -8,6 +8,7 @@ use Magento\FunctionalTestingFramework\DataGenerator\Objects\EntityDataObject; use Magento\FunctionalTestingFramework\DataGenerator\Handlers\DataObjectHandler; +use Magento\FunctionalTestingFramework\Exceptions\TestFrameworkException; /** * Class DataPersistenceHandler @@ -75,6 +76,7 @@ public function __construct($entityObject, $dependentObjects = [], $customFields * * @param string $storeCode * @return void + * @throws TestFrameworkException */ public function createEntity($storeCode = null) { @@ -97,6 +99,8 @@ public function createEntity($storeCode = null) * @param string $updateDataName * @param array $updateDependentObjects * @return void + * @throws TestFrameworkException + * @throws \Exception */ public function updateEntity($updateDataName, $updateDependentObjects = []) @@ -121,6 +125,7 @@ public function updateEntity($updateDataName, $updateDependentObjects = []) * @param integer|null $index * @param string $storeCode * @return void + * @throws TestFrameworkException */ public function getEntity($index = null, $storeCode = null) @@ -142,6 +147,7 @@ public function getEntity($index = null, $storeCode = null) * Function which executes a delete request based on specific operation metadata * * @return void + * @throws TestFrameworkException */ public function deleteEntity() { @@ -163,6 +169,7 @@ public function getCreatedObject() * Returns a specific data value based on the CreatedObject's definition. * @param string $dataName * @return string + * @throws TestFrameworkException */ public function getCreatedDataByName($dataName) { diff --git a/src/Magento/FunctionalTestingFramework/DataGenerator/Persist/OperationDataArrayResolver.php b/src/Magento/FunctionalTestingFramework/DataGenerator/Persist/OperationDataArrayResolver.php index 6097f64da..6f5a1b1d3 100644 --- a/src/Magento/FunctionalTestingFramework/DataGenerator/Persist/OperationDataArrayResolver.php +++ b/src/Magento/FunctionalTestingFramework/DataGenerator/Persist/OperationDataArrayResolver.php @@ -11,6 +11,7 @@ use Magento\FunctionalTestingFramework\DataGenerator\Objects\EntityDataObject; use Magento\FunctionalTestingFramework\DataGenerator\Objects\OperationElement; use Magento\FunctionalTestingFramework\DataGenerator\Util\OperationElementExtractor; +use Magento\FunctionalTestingFramework\Exceptions\TestFrameworkException; class OperationDataArrayResolver { @@ -174,6 +175,7 @@ public function resolveOperationDataArray($entityObject, $operationMetadata, $op * @param string $operationKey * @param string $operationElementType * @return array|string + * @throws TestFrameworkException */ private function resolvePrimitiveReference($entityObject, $operationKey, $operationElementType) { @@ -234,6 +236,7 @@ private function getDependentEntitiesOfType($type) * @param EntityDataObject $entityObject * @param string $operationElementValue * @return EntityDataObject|null + * @throws \Exception */ private function resolveOperationObjectAndEntityData($entityObject, $operationElementValue) { @@ -258,6 +261,7 @@ private function resolveOperationObjectAndEntityData($entityObject, $operationEl * @param string $operation * @param bool $fromArray * @return array + * @throws \Exception */ private function resolveNonPrimitiveElement($entityName, $operationElement, $operation, $fromArray = false) { @@ -290,6 +294,7 @@ private function resolveNonPrimitiveElement($entityName, $operationElement, $ope * * @param string $entityName * @return EntityDataObject + * @throws \Exception */ private function resolveLinkedEntityObject($entityName) { diff --git a/src/Magento/FunctionalTestingFramework/Page/Handlers/PageObjectHandler.php b/src/Magento/FunctionalTestingFramework/Page/Handlers/PageObjectHandler.php index 0c47ab7c3..caf2262fa 100644 --- a/src/Magento/FunctionalTestingFramework/Page/Handlers/PageObjectHandler.php +++ b/src/Magento/FunctionalTestingFramework/Page/Handlers/PageObjectHandler.php @@ -38,6 +38,8 @@ class PageObjectHandler implements ObjectHandlerInterface /** * Private constructor + * + * @throws XmlException */ private function __construct() { @@ -74,6 +76,7 @@ private function __construct() * Singleton method to return PageObjectHandler. * * @return PageObjectHandler + * @throws XmlException */ public static function getInstance() { diff --git a/src/Magento/FunctionalTestingFramework/Page/Handlers/SectionObjectHandler.php b/src/Magento/FunctionalTestingFramework/Page/Handlers/SectionObjectHandler.php index a361579e8..ec52e6eb5 100644 --- a/src/Magento/FunctionalTestingFramework/Page/Handlers/SectionObjectHandler.php +++ b/src/Magento/FunctionalTestingFramework/Page/Handlers/SectionObjectHandler.php @@ -43,6 +43,7 @@ class SectionObjectHandler implements ObjectHandlerInterface * Constructor * * @constructor + * @throws XmlException */ private function __construct() { @@ -93,6 +94,7 @@ private function __construct() * Initialize and/or return the singleton instance of this class * * @return SectionObjectHandler + * @throws XmlException */ public static function getInstance() { diff --git a/src/Magento/FunctionalTestingFramework/Page/Objects/PageObject.php b/src/Magento/FunctionalTestingFramework/Page/Objects/PageObject.php index 30cdc89ed..80e29496d 100644 --- a/src/Magento/FunctionalTestingFramework/Page/Objects/PageObject.php +++ b/src/Magento/FunctionalTestingFramework/Page/Objects/PageObject.php @@ -7,6 +7,7 @@ namespace Magento\FunctionalTestingFramework\Page\Objects; +use Magento\FunctionalTestingFramework\Exceptions\XmlException; use Magento\FunctionalTestingFramework\Page\Handlers\SectionObjectHandler; /** @@ -143,6 +144,7 @@ public function hasSection($sectionName) * * @param string $sectionName * @return SectionObject | null + * @throws XmlException */ public function getSection($sectionName) { diff --git a/src/Magento/FunctionalTestingFramework/Suite/Generators/GroupClassGenerator.php b/src/Magento/FunctionalTestingFramework/Suite/Generators/GroupClassGenerator.php index 5ba1c3cac..2c34d4e6e 100644 --- a/src/Magento/FunctionalTestingFramework/Suite/Generators/GroupClassGenerator.php +++ b/src/Magento/FunctionalTestingFramework/Suite/Generators/GroupClassGenerator.php @@ -6,6 +6,7 @@ namespace Magento\FunctionalTestingFramework\Suite\Generators; +use Magento\FunctionalTestingFramework\Exceptions\TestReferenceException; use Magento\FunctionalTestingFramework\Suite\Objects\SuiteObject; use Magento\FunctionalTestingFramework\Test\Objects\ActionObject; use Magento\FunctionalTestingFramework\Test\Objects\TestHookObject; @@ -69,6 +70,7 @@ public function __construct() * * @param SuiteObject $suiteObject * @return string + * @throws TestReferenceException */ public function generateGroupClass($suiteObject) { @@ -85,6 +87,7 @@ public function generateGroupClass($suiteObject) * * @param SuiteObject $suiteObject * @return string; + * @throws TestReferenceException */ private function createClassContent($suiteObject) { @@ -123,6 +126,7 @@ private function extractClassVar($beforeArray, $afterArray) * * @param TestHookObject $hookObj * @return array + * @throws TestReferenceException */ private function buildHookMustacheArray($hookObj) { @@ -170,6 +174,7 @@ private function buildHookMustacheArray($hookObj) * @param ActionObject $action * @param array $actionEntries * @return array + * @throws TestReferenceException */ private function buildWebDriverActionsMustacheArray($action, $actionEntries) { diff --git a/src/Magento/FunctionalTestingFramework/Suite/Handlers/SuiteObjectHandler.php b/src/Magento/FunctionalTestingFramework/Suite/Handlers/SuiteObjectHandler.php index 8930de98c..d4b044cf8 100644 --- a/src/Magento/FunctionalTestingFramework/Suite/Handlers/SuiteObjectHandler.php +++ b/src/Magento/FunctionalTestingFramework/Suite/Handlers/SuiteObjectHandler.php @@ -5,6 +5,7 @@ */ namespace Magento\FunctionalTestingFramework\Suite\Handlers; +use Magento\FunctionalTestingFramework\Exceptions\XmlException; use Magento\FunctionalTestingFramework\ObjectManager\ObjectHandlerInterface; use Magento\FunctionalTestingFramework\ObjectManagerFactory; use Magento\FunctionalTestingFramework\Suite\Objects\SuiteObject; @@ -43,6 +44,7 @@ private function __construct() * Function to enforce singleton design pattern * * @return ObjectHandlerInterface + * @throws XmlException */ public static function getInstance() { @@ -103,6 +105,7 @@ public function getAllTestReferences() * * @return void * @SuppressWarnings(PHPMD.UnusedPrivateMethod) + * @throws XmlException */ private function initSuiteData() { diff --git a/src/Magento/FunctionalTestingFramework/Suite/SuiteGenerator.php b/src/Magento/FunctionalTestingFramework/Suite/SuiteGenerator.php index 9c96742b4..6cbe2f831 100644 --- a/src/Magento/FunctionalTestingFramework/Suite/SuiteGenerator.php +++ b/src/Magento/FunctionalTestingFramework/Suite/SuiteGenerator.php @@ -7,6 +7,7 @@ namespace Magento\FunctionalTestingFramework\Suite; use Magento\FunctionalTestingFramework\Exceptions\TestReferenceException; +use Magento\FunctionalTestingFramework\Exceptions\XmlException; use Magento\FunctionalTestingFramework\Suite\Generators\GroupClassGenerator; use Magento\FunctionalTestingFramework\Suite\Handlers\SuiteObjectHandler; use Magento\FunctionalTestingFramework\Suite\Objects\SuiteObject; @@ -71,6 +72,7 @@ public static function getInstance() * * @param BaseTestManifest $testManifest * @return void + * @throws \Exception */ public function generateAllSuites($testManifest) { @@ -98,6 +100,7 @@ public function generateAllSuites($testManifest) * Returns an array of tests contained within suites as keys pointed at the name of their corresponding suite. * * @return array + * @throws XmlException */ public function getTestsReferencedInSuites() { @@ -131,6 +134,7 @@ public function getTestsReferencedInSuites() * * @param string $suiteName * @return void + * @throws \Exception */ public function generateSuite($suiteName) { @@ -147,6 +151,8 @@ public function generateSuite($suiteName) * @param array $tests * @param string $originalSuiteName * @return void + * @throws TestReferenceException + * @throws XmlException */ private function generateSuiteFromTest($suiteName, $tests = [], $originalSuiteName = null) { @@ -181,6 +187,7 @@ private function generateSuiteFromTest($suiteName, $tests = [], $originalSuiteNa * @param string $originalSuiteName * @return void * @throws TestReferenceException + * @throws XmlException */ private function validateTestsReferencedInSuite($suiteName, $testsReferenced, $originalSuiteName) { @@ -207,6 +214,7 @@ private function validateTestsReferencedInSuite($suiteName, $testsReferenced, $o * @param string $suiteName * @param array $suiteContent * @return void + * @throws \Exception */ private function generateSplitSuiteFromTest($suiteName, $suiteContent) { @@ -223,6 +231,8 @@ private function generateSplitSuiteFromTest($suiteName, $suiteContent) * @param array $tests * @param string $originalSuiteName * @return null|string + * @throws XmlException + * @throws TestReferenceException */ private function generateGroupFile($suiteName, $tests, $originalSuiteName) { @@ -323,6 +333,7 @@ private static function clearPreviousSessionConfigEntries() * @param string $path * @param array $tests * @return void + * @throws TestReferenceException */ private function generateRelevantGroupTests($path, $tests) { diff --git a/src/Magento/FunctionalTestingFramework/Suite/Util/SuiteObjectExtractor.php b/src/Magento/FunctionalTestingFramework/Suite/Util/SuiteObjectExtractor.php index 4db1abfc7..2e600806b 100644 --- a/src/Magento/FunctionalTestingFramework/Suite/Util/SuiteObjectExtractor.php +++ b/src/Magento/FunctionalTestingFramework/Suite/Util/SuiteObjectExtractor.php @@ -42,6 +42,7 @@ public function __construct() * @throws XmlException * @SuppressWarnings(PHPMD.CyclomaticComplexity) * @SuppressWarnings(PHPMD.NPathComplexity) + * @throws \Exception */ public function parseSuiteDataIntoObjects($parsedSuiteData) { @@ -133,6 +134,7 @@ public function parseSuiteDataIntoObjects($parsedSuiteData) * * @param array $suiteReferences * @return array + * @throws \Exception */ private function extractTestObjectsFromSuiteRef($suiteReferences) { @@ -169,6 +171,7 @@ private function extractTestObjectsFromSuiteRef($suiteReferences) * @param string $moduleName * @param string $moduleFilePath * @return array + * @throws \Exception */ private function extractModuleAndFiles($moduleName, $moduleFilePath) { @@ -219,6 +222,7 @@ private function resolveFilePathTestNames($filename, $moduleName = null) * * @param string $moduleName * @return array + * @throws \Exception */ private function resolveModulePathTestNames($moduleName) { diff --git a/src/Magento/FunctionalTestingFramework/Test/Handlers/TestObjectHandler.php b/src/Magento/FunctionalTestingFramework/Test/Handlers/TestObjectHandler.php index b301c7aaa..721c5df84 100644 --- a/src/Magento/FunctionalTestingFramework/Test/Handlers/TestObjectHandler.php +++ b/src/Magento/FunctionalTestingFramework/Test/Handlers/TestObjectHandler.php @@ -5,6 +5,7 @@ */ namespace Magento\FunctionalTestingFramework\Test\Handlers; +use Magento\FunctionalTestingFramework\Exceptions\XmlException; use Magento\FunctionalTestingFramework\ObjectManager\ObjectHandlerInterface; use Magento\FunctionalTestingFramework\ObjectManagerFactory; use Magento\FunctionalTestingFramework\Test\Objects\TestObject; @@ -36,6 +37,7 @@ class TestObjectHandler implements ObjectHandlerInterface * Singleton method to return TestObjectHandler. * * @return TestObjectHandler + * @throws XmlException */ public static function getInstance() { @@ -106,6 +108,7 @@ public function getTestsByGroup($groupName) * * @return void * @SuppressWarnings(PHPMD.UnusedPrivateMethod) + * @throws XmlException */ private function initTestData() { diff --git a/src/Magento/FunctionalTestingFramework/Test/Objects/ActionGroupObject.php b/src/Magento/FunctionalTestingFramework/Test/Objects/ActionGroupObject.php index fdef1107e..d1e8112e6 100644 --- a/src/Magento/FunctionalTestingFramework/Test/Objects/ActionGroupObject.php +++ b/src/Magento/FunctionalTestingFramework/Test/Objects/ActionGroupObject.php @@ -85,6 +85,7 @@ public function getSteps($arguments, $actionReferenceKey) * Iterates through given $arguments and overrides ActionGroup's argument values, if any are found. * @param array $arguments * @return ArgumentObject[] + * @throws TestReferenceException */ private function resolveArguments($arguments) { diff --git a/src/Magento/FunctionalTestingFramework/Test/Objects/ActionObject.php b/src/Magento/FunctionalTestingFramework/Test/Objects/ActionObject.php index 8a57013b1..fcba0b49d 100644 --- a/src/Magento/FunctionalTestingFramework/Test/Objects/ActionObject.php +++ b/src/Magento/FunctionalTestingFramework/Test/Objects/ActionObject.php @@ -233,6 +233,8 @@ public function setTimeout($timeout) * userInput * * @return void + * @throws TestReferenceException + * @throws XmlException */ public function resolveReferences() { @@ -253,6 +255,7 @@ public function resolveReferences() * Warns user if they are using old Assertion syntax. * * @return void + * @throws TestReferenceException */ public function trimAssertionAttributes() { @@ -326,6 +329,8 @@ private function validateAssertionSchema($attributes) * e.g. {{SomeSectionName.ElementName}} becomes #login-button * * @return void + * @throws XmlException + * @throws \Exception */ private function resolveSelectorReferenceAndTimeout() { @@ -352,6 +357,8 @@ private function resolveSelectorReferenceAndTimeout() * e.g. {{SomePageName}} becomes http://localhost:76543/some/url * * @return void + * @throws TestReferenceException + * @throws XmlException */ private function resolveUrlReference() { @@ -373,6 +380,8 @@ private function resolveUrlReference() * e.g. {{CustomerEntityFoo.FirstName}} becomes Jerry * * @return void + * @throws TestReferenceException + * @throws \Exception */ private function resolveDataInputReferences() { @@ -452,7 +461,7 @@ private function stripAndReturnParameters($reference) * @param ObjectHandlerInterface $objectHandler * @param string $inputString * @return string | null - * @throws \Exception + * @throws TestReferenceException | \Exception * * @SuppressWarnings(PHPMD.CyclomaticComplexity) */ @@ -584,6 +593,7 @@ private function resolveEntityDataObjectReference($obj, $match) * @param string $match * @param object $object * @return string + * @throws \Exception */ private function resolveParameterization($isParameterized, $replacement, $match, $object) { diff --git a/src/Magento/FunctionalTestingFramework/Test/Util/ActionGroupObjectExtractor.php b/src/Magento/FunctionalTestingFramework/Test/Util/ActionGroupObjectExtractor.php index aeef21760..32b54c3da 100644 --- a/src/Magento/FunctionalTestingFramework/Test/Util/ActionGroupObjectExtractor.php +++ b/src/Magento/FunctionalTestingFramework/Test/Util/ActionGroupObjectExtractor.php @@ -7,6 +7,7 @@ namespace Magento\FunctionalTestingFramework\Test\Util; use Magento\FunctionalTestingFramework\Data\Argument\Interpreter\Argument; +use Magento\FunctionalTestingFramework\Exceptions\XmlException; use Magento\FunctionalTestingFramework\Test\Objects\ActionGroupObject; use Magento\FunctionalTestingFramework\Test\Objects\ArgumentObject; @@ -39,6 +40,7 @@ public function __construct() * * @param array $actionGroupData * @return ActionGroupObject + * @throws XmlException */ public function extractActionGroup($actionGroupData) { diff --git a/src/Magento/FunctionalTestingFramework/Test/Util/ActionMergeUtil.php b/src/Magento/FunctionalTestingFramework/Test/Util/ActionMergeUtil.php index 8af5e3af0..4bc120f2e 100644 --- a/src/Magento/FunctionalTestingFramework/Test/Util/ActionMergeUtil.php +++ b/src/Magento/FunctionalTestingFramework/Test/Util/ActionMergeUtil.php @@ -71,6 +71,8 @@ public function __construct($contextName, $contextType) * @param array $parsedSteps * @param bool $skipActionGroupResolution * @return array + * @throws TestReferenceException + * @throws XmlException */ public function resolveActionSteps($parsedSteps, $skipActionGroupResolution = false) { @@ -119,6 +121,7 @@ private function resolveActionGroups($mergedSteps) * * @param array $parsedSteps * @return void + * @throws XmlException */ private function mergeActions($parsedSteps) { diff --git a/src/Magento/FunctionalTestingFramework/Test/Util/ActionObjectExtractor.php b/src/Magento/FunctionalTestingFramework/Test/Util/ActionObjectExtractor.php index a3ce58a5e..932e5c96e 100644 --- a/src/Magento/FunctionalTestingFramework/Test/Util/ActionObjectExtractor.php +++ b/src/Magento/FunctionalTestingFramework/Test/Util/ActionObjectExtractor.php @@ -48,6 +48,7 @@ public function __construct() * @param string $testName * @return array * @throws XmlException + * @throws TestReferenceException */ public function extractActions($testActions, $testName = null) { @@ -162,6 +163,8 @@ private function processActionGroupArgs($actionAttributeData) * @param array $actionData * @param array $actions * @return array + * @throws XmlException + * @throws TestReferenceException */ private function extractFieldActions($actionData, $actions) { diff --git a/src/Magento/FunctionalTestingFramework/Test/Util/TestHookObjectExtractor.php b/src/Magento/FunctionalTestingFramework/Test/Util/TestHookObjectExtractor.php index 16ba69e0b..0084800b5 100644 --- a/src/Magento/FunctionalTestingFramework/Test/Util/TestHookObjectExtractor.php +++ b/src/Magento/FunctionalTestingFramework/Test/Util/TestHookObjectExtractor.php @@ -37,6 +37,7 @@ public function __construct() * @param string $hookType * @param array $testHook * @return TestHookObject + * @throws \Exception */ public function extractHook($parentName, $hookType, $testHook) { diff --git a/src/Magento/FunctionalTestingFramework/Test/Util/TestObjectExtractor.php b/src/Magento/FunctionalTestingFramework/Test/Util/TestObjectExtractor.php index be4e3c97c..649376558 100644 --- a/src/Magento/FunctionalTestingFramework/Test/Util/TestObjectExtractor.php +++ b/src/Magento/FunctionalTestingFramework/Test/Util/TestObjectExtractor.php @@ -59,7 +59,7 @@ public function __construct() * * @param array $testData * @return TestObject - * @throws \Magento\FunctionalTestingFramework\Exceptions\XmlException + * @throws \Exception */ public function extractTestData($testData) { From 6a94c87537edd33547987d92292faab377f85494 Mon Sep 17 00:00:00 2001 From: KevinBKozan Date: Thu, 26 Apr 2018 13:30:44 -0500 Subject: [PATCH 004/111] MQE-980: [Annotations] tag defaults to module - Dom no longer overwrites filename, instead builds comma separated list to keep track of all test and merge paths - TestObjectExtractor not extracts module form base test path, and overwrites testAnnotations["features"] to only include the single module name. - Unit/Verification updates. --- .../Test/Handlers/TestObjectHandlerTest.php | 10 ++++++ dev/tests/unit/Util/TestDataArrayBuilder.php | 28 ++++++++++++++- .../ActionGroupUsingNestedArgument.txt | 1 + .../ActionGroupWithDataOverrideTest.txt | 2 +- .../Resources/ActionGroupWithDataTest.txt | 2 +- ...hDefaultArgumentAndStringSelectorParam.txt | 1 + ...eParameterSelectorsFromDefaultArgument.txt | 1 + .../Resources/ActionGroupWithNoArguments.txt | 1 + .../ActionGroupWithNoDefaultTest.txt | 2 +- ...thPassedArgumentAndStringSelectorParam.txt | 1 + .../ActionGroupWithPersistedData.txt | 2 +- ...WithSimpleDataUsageFromDefaultArgument.txt | 1 + ...pWithSimpleDataUsageFromPassedArgument.txt | 1 + ...leParameterSelectorFromDefaultArgument.txt | 1 + ...gleParameterSelectorFromPassedArgument.txt | 1 + .../ActionGroupWithStepKeyReferences.txt | 1 + .../ActionGroupWithTopLevelPersistedData.txt | 2 +- .../ArgumentWithSameNameAsElement.txt | 2 +- .../verification/Resources/AssertTest.txt | 1 + .../Resources/BasicActionGroupTest.txt | 2 +- .../Resources/BasicFunctionalTest.txt | 2 +- .../verification/Resources/BasicMergeTest.txt | 2 +- .../Resources/CharacterReplacementTest.txt | 1 + .../Resources/DataReplacementTest.txt | 1 + .../Resources/HookActionsTest.txt | 1 + .../Resources/LocatorFunctionTest.txt | 1 + .../Resources/MergedActionGroupTest.txt | 2 +- .../Resources/MergedReferencesTest.txt | 2 +- .../Resources/MultipleActionGroupsTest.txt | 2 +- .../Resources/PageReplacementTest.txt | 1 + .../Resources/ParameterArrayTest.txt | 1 + .../Resources/PersistedReplacementTest.txt | 1 + .../Resources/PersistenceCustomFieldsTest.txt | 1 + .../Resources/SectionReplacementTest.txt | 1 + .../FunctionalTestingFramework/Config/Dom.php | 8 +++++ .../Test/Util/TestObjectExtractor.php | 35 +++++++++++++++++++ 36 files changed, 112 insertions(+), 13 deletions(-) diff --git a/dev/tests/unit/Magento/FunctionalTestFramework/Test/Handlers/TestObjectHandlerTest.php b/dev/tests/unit/Magento/FunctionalTestFramework/Test/Handlers/TestObjectHandlerTest.php index a03fb4bc1..6ef9af12a 100644 --- a/dev/tests/unit/Magento/FunctionalTestFramework/Test/Handlers/TestObjectHandlerTest.php +++ b/dev/tests/unit/Magento/FunctionalTestFramework/Test/Handlers/TestObjectHandlerTest.php @@ -88,6 +88,7 @@ public function testGetTestObject() $testDataArrayBuilder->testName, [$expectedTestActionObject], [ + 'features' => ['NO MODULE DETECTED'], 'group' => ['test'] ], [ @@ -101,6 +102,15 @@ public function testGetTestObject() $this->assertEquals($expectedTestObject, $actualTestObject); } + /** + * Tests basic getting of a test that has a fileName + */ + public function testGetTestWithFileName() + { + $this->markTestIncomplete(); + //TODO + } + /** * Tests the function used to get a series of relevant tests by group. * diff --git a/dev/tests/unit/Util/TestDataArrayBuilder.php b/dev/tests/unit/Util/TestDataArrayBuilder.php index 2cbc5ccf4..2dc739b94 100644 --- a/dev/tests/unit/Util/TestDataArrayBuilder.php +++ b/dev/tests/unit/Util/TestDataArrayBuilder.php @@ -7,6 +7,7 @@ namespace tests\unit\Util; use Magento\FunctionalTestingFramework\Test\Util\ActionObjectExtractor; +use Magento\FunctionalTestingFramework\Test\Util\AnnotationExtractor; use Magento\FunctionalTestingFramework\Test\Util\TestObjectExtractor; class TestDataArrayBuilder @@ -18,6 +19,13 @@ class TestDataArrayBuilder */ public $testName = 'testTest'; + /** + * Mock file name + * + * @var string + */ + public $filename = null; + /** * Mock before action name * @@ -187,6 +195,23 @@ public function withTestActions($actions = null) return $this; } + /** + * Add file name passe in by arg (or default if no arg) + * @param string $filename + * @return $this + */ + public function withFileName($filename = null) + { + if ($filename == null) { + $this->filename = + "/magento2-functional-testing-framework/dev/tests/verification/TestModule/Test/BasicFunctionalTest.xml"; + } else { + $this->filename = $filename; + } + + return $this; + } + /** * Output the resulting test data array based on parameters set in the object * @@ -201,7 +226,8 @@ public function build() TestObjectExtractor::TEST_ANNOTATIONS => $this->annotations, TestObjectExtractor::TEST_BEFORE_HOOK => $this->beforeHook, TestObjectExtractor::TEST_AFTER_HOOK => $this->afterHook, - TestObjectExtractor::TEST_FAILED_HOOK => $this->failedHook + TestObjectExtractor::TEST_FAILED_HOOK => $this->failedHook, + "filename" => $this->filename ], $this->testActions )]; diff --git a/dev/tests/verification/Resources/ActionGroupUsingNestedArgument.txt b/dev/tests/verification/Resources/ActionGroupUsingNestedArgument.txt index b978a3188..a36c37e7a 100644 --- a/dev/tests/verification/Resources/ActionGroupUsingNestedArgument.txt +++ b/dev/tests/verification/Resources/ActionGroupUsingNestedArgument.txt @@ -20,6 +20,7 @@ use Yandex\Allure\Adapter\Annotation\TestCaseId; class ActionGroupUsingNestedArgumentCest { /** + * @Features({"TestModule"}) * @Parameter(name = "AcceptanceTester", value="$I") * @param AcceptanceTester $I * @return void diff --git a/dev/tests/verification/Resources/ActionGroupWithDataOverrideTest.txt b/dev/tests/verification/Resources/ActionGroupWithDataOverrideTest.txt index 0809289b3..0c4ff37af 100644 --- a/dev/tests/verification/Resources/ActionGroupWithDataOverrideTest.txt +++ b/dev/tests/verification/Resources/ActionGroupWithDataOverrideTest.txt @@ -61,7 +61,7 @@ class ActionGroupWithDataOverrideTestCest /** * @Severity(level = SeverityLevel::CRITICAL) - * @Features({"Action Group Functional Cest"}) + * @Features({"TestModule"}) * @Stories({"MQE-433"}) * @Parameter(name = "AcceptanceTester", value="$I") * @param AcceptanceTester $I diff --git a/dev/tests/verification/Resources/ActionGroupWithDataTest.txt b/dev/tests/verification/Resources/ActionGroupWithDataTest.txt index fe3cf57c1..5f7d75899 100644 --- a/dev/tests/verification/Resources/ActionGroupWithDataTest.txt +++ b/dev/tests/verification/Resources/ActionGroupWithDataTest.txt @@ -61,7 +61,7 @@ class ActionGroupWithDataTestCest /** * @Severity(level = SeverityLevel::CRITICAL) - * @Features({"Action Group Functional Cest"}) + * @Features({"TestModule"}) * @Stories({"MQE-433"}) * @Parameter(name = "AcceptanceTester", value="$I") * @param AcceptanceTester $I diff --git a/dev/tests/verification/Resources/ActionGroupWithDefaultArgumentAndStringSelectorParam.txt b/dev/tests/verification/Resources/ActionGroupWithDefaultArgumentAndStringSelectorParam.txt index 9c56afff4..421bb4af7 100644 --- a/dev/tests/verification/Resources/ActionGroupWithDefaultArgumentAndStringSelectorParam.txt +++ b/dev/tests/verification/Resources/ActionGroupWithDefaultArgumentAndStringSelectorParam.txt @@ -22,6 +22,7 @@ class ActionGroupWithDefaultArgumentAndStringSelectorParamCest { /** * @Severity(level = SeverityLevel::BLOCKER) + * @Features({"TestModule"}) * @Parameter(name = "AcceptanceTester", value="$I") * @param AcceptanceTester $I * @return void diff --git a/dev/tests/verification/Resources/ActionGroupWithMultipleParameterSelectorsFromDefaultArgument.txt b/dev/tests/verification/Resources/ActionGroupWithMultipleParameterSelectorsFromDefaultArgument.txt index aa213c80f..3f907b9ee 100644 --- a/dev/tests/verification/Resources/ActionGroupWithMultipleParameterSelectorsFromDefaultArgument.txt +++ b/dev/tests/verification/Resources/ActionGroupWithMultipleParameterSelectorsFromDefaultArgument.txt @@ -22,6 +22,7 @@ class ActionGroupWithMultipleParameterSelectorsFromDefaultArgumentCest { /** * @Severity(level = SeverityLevel::BLOCKER) + * @Features({"TestModule"}) * @Parameter(name = "AcceptanceTester", value="$I") * @param AcceptanceTester $I * @return void diff --git a/dev/tests/verification/Resources/ActionGroupWithNoArguments.txt b/dev/tests/verification/Resources/ActionGroupWithNoArguments.txt index 45807663a..b2fa98e5c 100644 --- a/dev/tests/verification/Resources/ActionGroupWithNoArguments.txt +++ b/dev/tests/verification/Resources/ActionGroupWithNoArguments.txt @@ -22,6 +22,7 @@ class ActionGroupWithNoArgumentsCest { /** * @Severity(level = SeverityLevel::BLOCKER) + * @Features({"TestModule"}) * @Parameter(name = "AcceptanceTester", value="$I") * @param AcceptanceTester $I * @return void diff --git a/dev/tests/verification/Resources/ActionGroupWithNoDefaultTest.txt b/dev/tests/verification/Resources/ActionGroupWithNoDefaultTest.txt index 5cc1d7f39..82fde4afd 100644 --- a/dev/tests/verification/Resources/ActionGroupWithNoDefaultTest.txt +++ b/dev/tests/verification/Resources/ActionGroupWithNoDefaultTest.txt @@ -61,7 +61,7 @@ class ActionGroupWithNoDefaultTestCest /** * @Severity(level = SeverityLevel::CRITICAL) - * @Features({"Action Group Functional Cest"}) + * @Features({"TestModule"}) * @Stories({"MQE-433"}) * @Parameter(name = "AcceptanceTester", value="$I") * @param AcceptanceTester $I diff --git a/dev/tests/verification/Resources/ActionGroupWithPassedArgumentAndStringSelectorParam.txt b/dev/tests/verification/Resources/ActionGroupWithPassedArgumentAndStringSelectorParam.txt index 043a71c94..c7c305cb8 100644 --- a/dev/tests/verification/Resources/ActionGroupWithPassedArgumentAndStringSelectorParam.txt +++ b/dev/tests/verification/Resources/ActionGroupWithPassedArgumentAndStringSelectorParam.txt @@ -22,6 +22,7 @@ class ActionGroupWithPassedArgumentAndStringSelectorParamCest { /** * @Severity(level = SeverityLevel::BLOCKER) + * @Features({"TestModule"}) * @Parameter(name = "AcceptanceTester", value="$I") * @param AcceptanceTester $I * @return void diff --git a/dev/tests/verification/Resources/ActionGroupWithPersistedData.txt b/dev/tests/verification/Resources/ActionGroupWithPersistedData.txt index 2707770d5..fcd4f74f9 100644 --- a/dev/tests/verification/Resources/ActionGroupWithPersistedData.txt +++ b/dev/tests/verification/Resources/ActionGroupWithPersistedData.txt @@ -61,7 +61,7 @@ class ActionGroupWithPersistedDataCest /** * @Severity(level = SeverityLevel::CRITICAL) - * @Features({"Action Group Functional Cest"}) + * @Features({"TestModule"}) * @Stories({"MQE-433"}) * @Parameter(name = "AcceptanceTester", value="$I") * @param AcceptanceTester $I diff --git a/dev/tests/verification/Resources/ActionGroupWithSimpleDataUsageFromDefaultArgument.txt b/dev/tests/verification/Resources/ActionGroupWithSimpleDataUsageFromDefaultArgument.txt index 3774e639d..f27695b2b 100644 --- a/dev/tests/verification/Resources/ActionGroupWithSimpleDataUsageFromDefaultArgument.txt +++ b/dev/tests/verification/Resources/ActionGroupWithSimpleDataUsageFromDefaultArgument.txt @@ -22,6 +22,7 @@ class ActionGroupWithSimpleDataUsageFromDefaultArgumentCest { /** * @Severity(level = SeverityLevel::CRITICAL) + * @Features({"TestModule"}) * @Parameter(name = "AcceptanceTester", value="$I") * @param AcceptanceTester $I * @return void diff --git a/dev/tests/verification/Resources/ActionGroupWithSimpleDataUsageFromPassedArgument.txt b/dev/tests/verification/Resources/ActionGroupWithSimpleDataUsageFromPassedArgument.txt index a1e5a316b..ac56ca3f3 100644 --- a/dev/tests/verification/Resources/ActionGroupWithSimpleDataUsageFromPassedArgument.txt +++ b/dev/tests/verification/Resources/ActionGroupWithSimpleDataUsageFromPassedArgument.txt @@ -22,6 +22,7 @@ class ActionGroupWithSimpleDataUsageFromPassedArgumentCest { /** * @Severity(level = SeverityLevel::CRITICAL) + * @Features({"TestModule"}) * @Parameter(name = "AcceptanceTester", value="$I") * @param AcceptanceTester $I * @return void diff --git a/dev/tests/verification/Resources/ActionGroupWithSingleParameterSelectorFromDefaultArgument.txt b/dev/tests/verification/Resources/ActionGroupWithSingleParameterSelectorFromDefaultArgument.txt index d04e2ba80..7b74e0c1d 100644 --- a/dev/tests/verification/Resources/ActionGroupWithSingleParameterSelectorFromDefaultArgument.txt +++ b/dev/tests/verification/Resources/ActionGroupWithSingleParameterSelectorFromDefaultArgument.txt @@ -22,6 +22,7 @@ class ActionGroupWithSingleParameterSelectorFromDefaultArgumentCest { /** * @Severity(level = SeverityLevel::BLOCKER) + * @Features({"TestModule"}) * @Parameter(name = "AcceptanceTester", value="$I") * @param AcceptanceTester $I * @return void diff --git a/dev/tests/verification/Resources/ActionGroupWithSingleParameterSelectorFromPassedArgument.txt b/dev/tests/verification/Resources/ActionGroupWithSingleParameterSelectorFromPassedArgument.txt index fb10e7847..bd71ebdf7 100644 --- a/dev/tests/verification/Resources/ActionGroupWithSingleParameterSelectorFromPassedArgument.txt +++ b/dev/tests/verification/Resources/ActionGroupWithSingleParameterSelectorFromPassedArgument.txt @@ -22,6 +22,7 @@ class ActionGroupWithSingleParameterSelectorFromPassedArgumentCest { /** * @Severity(level = SeverityLevel::BLOCKER) + * @Features({"TestModule"}) * @Parameter(name = "AcceptanceTester", value="$I") * @param AcceptanceTester $I * @return void diff --git a/dev/tests/verification/Resources/ActionGroupWithStepKeyReferences.txt b/dev/tests/verification/Resources/ActionGroupWithStepKeyReferences.txt index ac97746b8..7b1a4d30d 100644 --- a/dev/tests/verification/Resources/ActionGroupWithStepKeyReferences.txt +++ b/dev/tests/verification/Resources/ActionGroupWithStepKeyReferences.txt @@ -20,6 +20,7 @@ use Yandex\Allure\Adapter\Annotation\TestCaseId; class ActionGroupWithStepKeyReferencesCest { /** + * @Features({"TestModule"}) * @Parameter(name = "AcceptanceTester", value="$I") * @param AcceptanceTester $I * @return void diff --git a/dev/tests/verification/Resources/ActionGroupWithTopLevelPersistedData.txt b/dev/tests/verification/Resources/ActionGroupWithTopLevelPersistedData.txt index 743864b79..6588d490b 100644 --- a/dev/tests/verification/Resources/ActionGroupWithTopLevelPersistedData.txt +++ b/dev/tests/verification/Resources/ActionGroupWithTopLevelPersistedData.txt @@ -61,7 +61,7 @@ class ActionGroupWithTopLevelPersistedDataCest /** * @Severity(level = SeverityLevel::CRITICAL) - * @Features({"Action Group Functional Cest"}) + * @Features({"TestModule"}) * @Stories({"MQE-433"}) * @Parameter(name = "AcceptanceTester", value="$I") * @param AcceptanceTester $I diff --git a/dev/tests/verification/Resources/ArgumentWithSameNameAsElement.txt b/dev/tests/verification/Resources/ArgumentWithSameNameAsElement.txt index bb59a6758..bc2890967 100644 --- a/dev/tests/verification/Resources/ArgumentWithSameNameAsElement.txt +++ b/dev/tests/verification/Resources/ArgumentWithSameNameAsElement.txt @@ -61,7 +61,7 @@ class ArgumentWithSameNameAsElementCest /** * @Severity(level = SeverityLevel::CRITICAL) - * @Features({"Action Group Functional Cest"}) + * @Features({"TestModule"}) * @Stories({"MQE-433"}) * @Parameter(name = "AcceptanceTester", value="$I") * @param AcceptanceTester $I diff --git a/dev/tests/verification/Resources/AssertTest.txt b/dev/tests/verification/Resources/AssertTest.txt index 60eed6a8d..111b236ed 100644 --- a/dev/tests/verification/Resources/AssertTest.txt +++ b/dev/tests/verification/Resources/AssertTest.txt @@ -37,6 +37,7 @@ class AssertTestCest } /** + * @Features({"TestModule"}) * @Parameter(name = "AcceptanceTester", value="$I") * @param AcceptanceTester $I * @return void diff --git a/dev/tests/verification/Resources/BasicActionGroupTest.txt b/dev/tests/verification/Resources/BasicActionGroupTest.txt index 86835364e..45ccc72a6 100644 --- a/dev/tests/verification/Resources/BasicActionGroupTest.txt +++ b/dev/tests/verification/Resources/BasicActionGroupTest.txt @@ -42,7 +42,7 @@ class BasicActionGroupTestCest /** * @Severity(level = SeverityLevel::CRITICAL) - * @Features({"Action Group Functional Cest"}) + * @Features({"TestModule"}) * @Stories({"MQE-433"}) * @Parameter(name = "AcceptanceTester", value="$I") * @param AcceptanceTester $I diff --git a/dev/tests/verification/Resources/BasicFunctionalTest.txt b/dev/tests/verification/Resources/BasicFunctionalTest.txt index 57bd9dd01..b826ecef8 100644 --- a/dev/tests/verification/Resources/BasicFunctionalTest.txt +++ b/dev/tests/verification/Resources/BasicFunctionalTest.txt @@ -50,7 +50,7 @@ class BasicFunctionalTestCest /** * @Severity(level = SeverityLevel::CRITICAL) - * @Features({"Basic Functional Cest"}) + * @Features({"TestModule"}) * @Stories({"MQE-305"}) * @Parameter(name = "AcceptanceTester", value="$I") * @param AcceptanceTester $I diff --git a/dev/tests/verification/Resources/BasicMergeTest.txt b/dev/tests/verification/Resources/BasicMergeTest.txt index 2cecb803f..ae3a69096 100644 --- a/dev/tests/verification/Resources/BasicMergeTest.txt +++ b/dev/tests/verification/Resources/BasicMergeTest.txt @@ -52,7 +52,7 @@ class BasicMergeTestCest /** * @Severity(level = SeverityLevel::CRITICAL) - * @Features({"Merge Functional Cest"}) + * @Features({"TestModule"}) * @Stories({"MQE-433"}) * @Parameter(name = "AcceptanceTester", value="$I") * @param AcceptanceTester $I diff --git a/dev/tests/verification/Resources/CharacterReplacementTest.txt b/dev/tests/verification/Resources/CharacterReplacementTest.txt index 4ae325fba..466997a6f 100644 --- a/dev/tests/verification/Resources/CharacterReplacementTest.txt +++ b/dev/tests/verification/Resources/CharacterReplacementTest.txt @@ -20,6 +20,7 @@ use Yandex\Allure\Adapter\Annotation\TestCaseId; class CharacterReplacementTestCest { /** + * @Features({"TestModule"}) * @Parameter(name = "AcceptanceTester", value="$I") * @param AcceptanceTester $I * @return void diff --git a/dev/tests/verification/Resources/DataReplacementTest.txt b/dev/tests/verification/Resources/DataReplacementTest.txt index d5121931c..07b733ff4 100644 --- a/dev/tests/verification/Resources/DataReplacementTest.txt +++ b/dev/tests/verification/Resources/DataReplacementTest.txt @@ -20,6 +20,7 @@ use Yandex\Allure\Adapter\Annotation\TestCaseId; class DataReplacementTestCest { /** + * @Features({"TestModule"}) * @Parameter(name = "AcceptanceTester", value="$I") * @param AcceptanceTester $I * @return void diff --git a/dev/tests/verification/Resources/HookActionsTest.txt b/dev/tests/verification/Resources/HookActionsTest.txt index 2e9eb609b..e15918c1c 100644 --- a/dev/tests/verification/Resources/HookActionsTest.txt +++ b/dev/tests/verification/Resources/HookActionsTest.txt @@ -76,6 +76,7 @@ class HookActionsTestCest } /** + * @Features({"TestModule"}) * @Parameter(name = "AcceptanceTester", value="$I") * @param AcceptanceTester $I * @return void diff --git a/dev/tests/verification/Resources/LocatorFunctionTest.txt b/dev/tests/verification/Resources/LocatorFunctionTest.txt index 37e5ae9fb..62fd808c5 100644 --- a/dev/tests/verification/Resources/LocatorFunctionTest.txt +++ b/dev/tests/verification/Resources/LocatorFunctionTest.txt @@ -20,6 +20,7 @@ use Yandex\Allure\Adapter\Annotation\TestCaseId; class LocatorFunctionTestCest { /** + * @Features({"TestModule"}) * @Parameter(name = "AcceptanceTester", value="$I") * @param AcceptanceTester $I * @return void diff --git a/dev/tests/verification/Resources/MergedActionGroupTest.txt b/dev/tests/verification/Resources/MergedActionGroupTest.txt index ab9b46f8d..59f2b2643 100644 --- a/dev/tests/verification/Resources/MergedActionGroupTest.txt +++ b/dev/tests/verification/Resources/MergedActionGroupTest.txt @@ -61,7 +61,7 @@ class MergedActionGroupTestCest /** * @Severity(level = SeverityLevel::CRITICAL) - * @Features({"Action Group Functional Cest"}) + * @Features({"TestModule"}) * @Stories({"MQE-433"}) * @Parameter(name = "AcceptanceTester", value="$I") * @param AcceptanceTester $I diff --git a/dev/tests/verification/Resources/MergedReferencesTest.txt b/dev/tests/verification/Resources/MergedReferencesTest.txt index 519db4314..530a23b2e 100644 --- a/dev/tests/verification/Resources/MergedReferencesTest.txt +++ b/dev/tests/verification/Resources/MergedReferencesTest.txt @@ -50,7 +50,7 @@ class MergedReferencesTestCest /** * @Severity(level = SeverityLevel::CRITICAL) - * @Features({"Merge Functional Cest"}) + * @Features({"TestModule"}) * @Stories({"MQE-433"}) * @Parameter(name = "AcceptanceTester", value="$I") * @param AcceptanceTester $I diff --git a/dev/tests/verification/Resources/MultipleActionGroupsTest.txt b/dev/tests/verification/Resources/MultipleActionGroupsTest.txt index d9b32bfce..8bb180f5d 100644 --- a/dev/tests/verification/Resources/MultipleActionGroupsTest.txt +++ b/dev/tests/verification/Resources/MultipleActionGroupsTest.txt @@ -61,7 +61,7 @@ class MultipleActionGroupsTestCest /** * @Severity(level = SeverityLevel::CRITICAL) - * @Features({"Action Group Functional Cest"}) + * @Features({"TestModule"}) * @Stories({"MQE-433"}) * @Parameter(name = "AcceptanceTester", value="$I") * @param AcceptanceTester $I diff --git a/dev/tests/verification/Resources/PageReplacementTest.txt b/dev/tests/verification/Resources/PageReplacementTest.txt index 5621e5c7a..f9fda51b2 100644 --- a/dev/tests/verification/Resources/PageReplacementTest.txt +++ b/dev/tests/verification/Resources/PageReplacementTest.txt @@ -20,6 +20,7 @@ use Yandex\Allure\Adapter\Annotation\TestCaseId; class PageReplacementTestCest { /** + * @Features({"TestModule"}) * @Parameter(name = "AcceptanceTester", value="$I") * @param AcceptanceTester $I * @return void diff --git a/dev/tests/verification/Resources/ParameterArrayTest.txt b/dev/tests/verification/Resources/ParameterArrayTest.txt index f60478037..d56a13386 100644 --- a/dev/tests/verification/Resources/ParameterArrayTest.txt +++ b/dev/tests/verification/Resources/ParameterArrayTest.txt @@ -20,6 +20,7 @@ use Yandex\Allure\Adapter\Annotation\TestCaseId; class ParameterArrayTestCest { /** + * @Features({"TestModule"}) * @Parameter(name = "AcceptanceTester", value="$I") * @param AcceptanceTester $I * @return void diff --git a/dev/tests/verification/Resources/PersistedReplacementTest.txt b/dev/tests/verification/Resources/PersistedReplacementTest.txt index 2c335c7f0..4e85b0085 100644 --- a/dev/tests/verification/Resources/PersistedReplacementTest.txt +++ b/dev/tests/verification/Resources/PersistedReplacementTest.txt @@ -37,6 +37,7 @@ class PersistedReplacementTestCest } /** + * @Features({"TestModule"}) * @Parameter(name = "AcceptanceTester", value="$I") * @param AcceptanceTester $I * @return void diff --git a/dev/tests/verification/Resources/PersistenceCustomFieldsTest.txt b/dev/tests/verification/Resources/PersistenceCustomFieldsTest.txt index 53db9bcf8..51da945b2 100644 --- a/dev/tests/verification/Resources/PersistenceCustomFieldsTest.txt +++ b/dev/tests/verification/Resources/PersistenceCustomFieldsTest.txt @@ -49,6 +49,7 @@ class PersistenceCustomFieldsTestCest } /** + * @Features({"TestModule"}) * @Parameter(name = "AcceptanceTester", value="$I") * @param AcceptanceTester $I * @return void diff --git a/dev/tests/verification/Resources/SectionReplacementTest.txt b/dev/tests/verification/Resources/SectionReplacementTest.txt index 07a8e7400..f0c71a7fa 100644 --- a/dev/tests/verification/Resources/SectionReplacementTest.txt +++ b/dev/tests/verification/Resources/SectionReplacementTest.txt @@ -20,6 +20,7 @@ use Yandex\Allure\Adapter\Annotation\TestCaseId; class SectionReplacementTestCest { /** + * @Features({"TestModule"}) * @Parameter(name = "AcceptanceTester", value="$I") * @param AcceptanceTester $I * @return void diff --git a/src/Magento/FunctionalTestingFramework/Config/Dom.php b/src/Magento/FunctionalTestingFramework/Config/Dom.php index 4bdfda289..e46d24214 100644 --- a/src/Magento/FunctionalTestingFramework/Config/Dom.php +++ b/src/Magento/FunctionalTestingFramework/Config/Dom.php @@ -215,6 +215,14 @@ protected function isTextNode($node) protected function mergeAttributes($baseNode, $mergeNode) { foreach ($mergeNode->attributes as $attribute) { + // Do not overwrite filename of base node + if ($attribute->name === "filename") { + $baseNode->setAttribute( + $this->getAttributeName($attribute), + $baseNode->getAttribute("filename") . "," . $attribute->value + ); + continue; + } $baseNode->setAttribute($this->getAttributeName($attribute), $attribute->value); } } diff --git a/src/Magento/FunctionalTestingFramework/Test/Util/TestObjectExtractor.php b/src/Magento/FunctionalTestingFramework/Test/Util/TestObjectExtractor.php index be4e3c97c..7fa21c085 100644 --- a/src/Magento/FunctionalTestingFramework/Test/Util/TestObjectExtractor.php +++ b/src/Magento/FunctionalTestingFramework/Test/Util/TestObjectExtractor.php @@ -69,6 +69,9 @@ public function extractTestData($testData) $testAnnotations = []; $testHooks = []; $filename = $testData['filename'] ?? null; + $fileNames = explode(",", $filename); + $baseFileName = $fileNames[0]; + $module = $this->extractModuleName($baseFileName); $testActions = $this->stripDescriptorTags( $testData, self::NODE_NAME, @@ -84,6 +87,9 @@ public function extractTestData($testData) $testAnnotations = $this->annotationExtractor->extractAnnotations($testData[self::TEST_ANNOTATIONS]); } + //Override features with module name if present, populates it otherwise + $testAnnotations["features"] = [$module]; + // extract before if (array_key_exists(self::TEST_BEFORE_HOOK, $testData)) { $testHooks[self::TEST_BEFORE_HOOK] = $this->testHookObjectExtractor->extractHook( @@ -120,4 +126,33 @@ public function extractTestData($testData) throw new XmlException($exception->getMessage() . ' in Test ' . $filename); } } + + /** + * Extracts module name from the path given + * @param string $path + * @return string + */ + private function extractModuleName($path) + { + if ($path === "") { + return "NO MODULE DETECTED"; + } + $positions = []; + $lastPos = 0; + while (($lastPos = strpos($path, DIRECTORY_SEPARATOR, $lastPos))!== false) { + $positions[] = $lastPos; + $lastPos = $lastPos + strlen(DIRECTORY_SEPARATOR); + } + $slashBeforeModule = $positions[count($positions)-3]; + $slashAfterModule = $positions[count($positions)-2]; + $output = substr($path, $slashBeforeModule+1, $slashAfterModule-$slashBeforeModule-1); + + //Check if file was actually from app/code or vendor + if ($output === "Mftf") { + $slashBeforeModule = $positions[count($positions)-5]; + $slashAfterModule = $positions[count($positions)-4]; + $output = substr($path, $slashBeforeModule+1, $slashAfterModule-$slashBeforeModule-1); + } + return $output; + } } From 35dd1fe9235e242f479191b5912444212f9c7ebf Mon Sep 17 00:00:00 2001 From: Alexander Calandra Date: Thu, 26 Apr 2018 13:43:48 -0500 Subject: [PATCH 005/111] MQE-982: Verification Test around Suite Generation - Adding Tests for Parallel Suite Generation and Suite Hook Verification --- dev/_suite/functionalSuiteHooks.xml | 21 +++ .../Resources/functionalSuiteHooks.txt | 104 +++++++++++++ .../Tests/SuiteGenerationTest.php | 139 ++++++++++++++++-- 3 files changed, 255 insertions(+), 9 deletions(-) create mode 100644 dev/_suite/functionalSuiteHooks.xml create mode 100644 dev/tests/verification/Resources/functionalSuiteHooks.txt diff --git a/dev/_suite/functionalSuiteHooks.xml b/dev/_suite/functionalSuiteHooks.xml new file mode 100644 index 000000000..1efa9c653 --- /dev/null +++ b/dev/_suite/functionalSuiteHooks.xml @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + diff --git a/dev/tests/verification/Resources/functionalSuiteHooks.txt b/dev/tests/verification/Resources/functionalSuiteHooks.txt new file mode 100644 index 000000000..f9aea6ef4 --- /dev/null +++ b/dev/tests/verification/Resources/functionalSuiteHooks.txt @@ -0,0 +1,104 @@ +currentTestRun++; + $this->executePreConditions(); + + if ($this->preconditionFailure != null) { + //if our preconditions fail, we need to mark all the tests as incomplete. + $e->getTest()->getMetadata()->setIncomplete($this->preconditionFailure); + } + } + + + private function executePreConditions() + { + if ($this->currentTestRun == 1) { + print sprintf(self::$HOOK_EXECUTION_INIT, "before"); + + try { + $webDriver = $this->getModule('\Magento\FunctionalTestingFramework\Module\MagentoWebDriver'); + + // close any open sessions + if ($webDriver->webDriver != null) { + $webDriver->webDriver->close(); + $webDriver->webDriver = null; + } + + // initialize the webdriver session + $webDriver->_initializeSession(); + $webDriver->amOnPage("some.url"); + + // reset configuration and close session + $this->getModule('\Magento\FunctionalTestingFramework\Module\MagentoWebDriver')->_resetConfig(); + $webDriver->webDriver->close(); + $webDriver->webDriver = null; + } catch (\Exception $exception) { + $this->preconditionFailure = $exception->getMessage(); + } + + print sprintf(self::$HOOK_EXECUTION_END, "before"); + } + } + + public function _after(\Codeception\Event\TestEvent $e) + { + $this->executePostConditions(); + } + + + private function executePostConditions() + { + if ($this->currentTestRun == $this->testCount) { + print sprintf(self::$HOOK_EXECUTION_INIT, "after"); + + try { + $webDriver = $this->getModule('\Magento\FunctionalTestingFramework\Module\MagentoWebDriver'); + + // close any open sessions + if ($webDriver->webDriver != null) { + $webDriver->webDriver->close(); + $webDriver->webDriver = null; + } + + // initialize the webdriver session + $webDriver->_initializeSession(); + $webDriver->amOnPage("some.url"); + + // reset configuration and close session + $this->getModule('\Magento\FunctionalTestingFramework\Module\MagentoWebDriver')->_resetConfig(); + $webDriver->webDriver->close(); + $webDriver->webDriver = null; + } catch (\Exception $exception) { + print $exception->getMessage(); + } + + print sprintf(self::$HOOK_EXECUTION_END, "after"); + } + } +} diff --git a/dev/tests/verification/Tests/SuiteGenerationTest.php b/dev/tests/verification/Tests/SuiteGenerationTest.php index b1790ac81..e018ba820 100644 --- a/dev/tests/verification/Tests/SuiteGenerationTest.php +++ b/dev/tests/verification/Tests/SuiteGenerationTest.php @@ -8,7 +8,9 @@ use Magento\Framework\Module\Dir; use Magento\FunctionalTestingFramework\Suite\SuiteGenerator; -use Magento\FunctionalTestingFramework\Util\TestManifest; +use Magento\FunctionalTestingFramework\Util\Filesystem\DirSetupUtil; +use Magento\FunctionalTestingFramework\Util\Manifest\ParallelTestManifest; +use Magento\FunctionalTestingFramework\Util\Manifest\TestManifestFactory; use Symfony\Component\Yaml\Yaml; use tests\util\MftfTestCase; @@ -16,6 +18,12 @@ class SuiteGenerationTest extends MftfTestCase { const RESOURCES_DIR = TESTS_BP . DIRECTORY_SEPARATOR . 'verification' . DIRECTORY_SEPARATOR . 'Resources'; const CONFIG_YML_FILE = FW_BP . DIRECTORY_SEPARATOR . SuiteGenerator::YAML_CODECEPTION_CONFIG_FILENAME; + const GENERATE_RESULT_DIR = TESTS_BP . + DIRECTORY_SEPARATOR . + "verification" . + DIRECTORY_SEPARATOR . + "_generated" . + DIRECTORY_SEPARATOR; /** * Flag to track existence of config.yml file @@ -75,11 +83,94 @@ public function testSuiteGeneration1() $yml = Yaml::parse(file_get_contents(self::CONFIG_YML_FILE)); $this->assertArrayHasKey($groupName, $yml['groups']); - $suiteResultBaseDir = TESTS_BP . + $suiteResultBaseDir = self::GENERATE_RESULT_DIR . DIRECTORY_SEPARATOR . - "verification" . - DIRECTORY_SEPARATOR . - "_generated" . + $groupName . + DIRECTORY_SEPARATOR; + + // Validate tests have been generated + $dirContents = array_diff(scandir($suiteResultBaseDir), ['..', '.']); + + foreach ($expectedContents as $expectedFile) { + $this->assertTrue(in_array($expectedFile, $dirContents)); + } + } + + /** + * Test generation of parallel suite groups + */ + public function testSuiteGenerationParallel() + { + $groupName = 'functionalSuite1'; + + $expectedGroups = [ + 'functionalSuite1_0', + 'functionalSuite1_1', + 'functionalSuite1_2', + 'functionalSuite1_3' + ]; + + $expectedContents = [ + 'additionalTestCest.php', + 'additionalIncludeTest2Cest.php', + 'IncludeTest2Cest.php', + 'IncludeTestCest.php' + ]; + + //createParallelManifest + /** @var ParallelTestManifest $parallelManifest */ + $parallelManifest = TestManifestFactory::makeManifest("parallel", ["functionalSuite1" => []]); + + // Generate the Suite + $parallelManifest->createTestGroups(1); + SuiteGenerator::getInstance()->generateAllSuites($parallelManifest); + + // Validate console message and add group name for later deletion + $this->expectOutputRegex('/Suite .* generated to .*/'); + self::$TEST_GROUPS[] = $groupName; + + // Validate Yaml file updated + $yml = Yaml::parse(file_get_contents(self::CONFIG_YML_FILE)); + $this->assertEquals($expectedGroups, array_keys($yml['groups'])); + + foreach ($expectedGroups as $expectedFolder) { + $suiteResultBaseDir = self::GENERATE_RESULT_DIR . + DIRECTORY_SEPARATOR . + $expectedFolder . + DIRECTORY_SEPARATOR; + + // Validate tests have been generated + $dirContents = array_diff(scandir($suiteResultBaseDir), ['..', '.']); + + //Validate only one test has been added to each group since lines are set to 1 + $this->assertEquals(1, count($dirContents)); + $this->assertContains(array_values($dirContents)[0], $expectedContents); + } + } + + /** + * Test hook groups generated during suite generation + */ + public function testSuiteGenerationHooks() + { + $groupName = 'functionalSuiteHooks'; + + $expectedContents = [ + 'IncludeTestCest.php' + ]; + + // Generate the Suite + SuiteGenerator::getInstance()->generateSuite($groupName); + + // Validate console message and add group name for later deletion + $this->expectOutputRegex('/Suite .* generated to .*/'); + self::$TEST_GROUPS[] = $groupName; + + // Validate Yaml file updated + $yml = Yaml::parse(file_get_contents(self::CONFIG_YML_FILE)); + $this->assertArrayHasKey($groupName, $yml['groups']); + + $suiteResultBaseDir = self::GENERATE_RESULT_DIR . DIRECTORY_SEPARATOR . $groupName . DIRECTORY_SEPARATOR; @@ -90,25 +181,55 @@ public function testSuiteGeneration1() foreach ($expectedContents as $expectedFile) { $this->assertTrue(in_array($expectedFile, $dirContents)); } + + //assert group file created and contains correct contents + $groupFile = PROJECT_ROOT . + DIRECTORY_SEPARATOR . + "src" . + DIRECTORY_SEPARATOR . + "Magento" . + DIRECTORY_SEPARATOR . + "FunctionalTestingFramework" . + DIRECTORY_SEPARATOR . + "Group" . + DIRECTORY_SEPARATOR . + $groupName . + ".php"; + + $this->assertTrue(file_exists($groupFile)); + $this->assertFileEquals( + self::RESOURCES_PATH . DIRECTORY_SEPARATOR . $groupName . ".txt", + $groupFile + ); + } /** * revert any changes made to config.yml + * remove _generated directory */ - public static function tearDownAfterClass() + public function tearDown() { // restore config if we see there was an original codeception.yml file if (self::$YML_EXISTS_FLAG) { $yml = Yaml::parse(file_get_contents(self::CONFIG_YML_FILE)); foreach (self::$TEST_GROUPS as $testGroup) { - unset($yml['group'][$testGroup]); + unset($yml['groups'][$testGroup]); } file_put_contents(self::CONFIG_YML_FILE, Yaml::dump($yml, 10)); - return; } + DirSetupUtil::rmdirRecursive(self::GENERATE_RESULT_DIR); + } - unlink(self::CONFIG_YML_FILE); + /** + * Remove yml if created during tests and did not exist before + */ + public static function tearDownAfterClass() + { + if (!self::$YML_EXISTS_FLAG) { + unlink(self::CONFIG_YML_FILE); + } } /** From 9771e645108f9c1511e181174efd3ed3ac3be174 Mon Sep 17 00:00:00 2001 From: Alexander Calandra Date: Thu, 26 Apr 2018 14:36:19 -0500 Subject: [PATCH 006/111] MQE-982: Verification Test around Suite Generation - Added SingleRun Manifest Verification - Sanitized createGroupDir for multiple entry points --- dev/_suite/functionalSuite.xml | 11 +++++ .../Tests/SuiteGenerationTest.php | 48 +++++++++++++++++++ .../Util/Filesystem/DirSetupUtil.php | 12 +++-- 3 files changed, 66 insertions(+), 5 deletions(-) diff --git a/dev/_suite/functionalSuite.xml b/dev/_suite/functionalSuite.xml index ab7816e4c..1a7326664 100644 --- a/dev/_suite/functionalSuite.xml +++ b/dev/_suite/functionalSuite.xml @@ -18,4 +18,15 @@ + + + + + + + + + + + diff --git a/dev/tests/verification/Tests/SuiteGenerationTest.php b/dev/tests/verification/Tests/SuiteGenerationTest.php index e018ba820..60298afdf 100644 --- a/dev/tests/verification/Tests/SuiteGenerationTest.php +++ b/dev/tests/verification/Tests/SuiteGenerationTest.php @@ -204,6 +204,54 @@ public function testSuiteGenerationHooks() } + /** + * Test generation of parallel suite groups + */ + public function testSuiteGenerationSingleRun() + { + //using functionalSuite2 to avoid directory caching + $groupName = 'functionalSuite2'; + + $expectedContents = [ + 'additionalTestCest.php', + 'additionalIncludeTest2Cest.php', + 'IncludeTest2Cest.php', + 'IncludeTestCest.php' + ]; + + //createParallelManifest + /** @var ParallelTestManifest $parallelManifest */ + $singleRunManifest = TestManifestFactory::makeManifest("singleRun", ["functionalSuite2" => []]); + + // Generate the Suite + SuiteGenerator::getInstance()->generateAllSuites($singleRunManifest); + $singleRunManifest->generate(); + + // Validate console message and add group name for later deletion + $this->expectOutputRegex('/Suite .* generated to .*/'); + self::$TEST_GROUPS[] = $groupName; + + // Validate Yaml file updated + $yml = Yaml::parse(file_get_contents(self::CONFIG_YML_FILE)); + $this->assertArrayHasKey($groupName, $yml['groups']); + + $suiteResultBaseDir = self::GENERATE_RESULT_DIR . + DIRECTORY_SEPARATOR . + $groupName . + DIRECTORY_SEPARATOR; + + // Validate tests have been generated + $dirContents = array_diff(scandir($suiteResultBaseDir), ['..', '.']); + + foreach ($expectedContents as $expectedFile) { + $this->assertTrue(in_array($expectedFile, $dirContents)); + } + + $expectedManifest = "dev/tests/verification/_generated/default/" . PHP_EOL . "-g functionalSuite2" . PHP_EOL; + + $this->assertEquals($expectedManifest, file_get_contents(self::getManifestFilePath())); + } + /** * revert any changes made to config.yml * remove _generated directory diff --git a/src/Magento/FunctionalTestingFramework/Util/Filesystem/DirSetupUtil.php b/src/Magento/FunctionalTestingFramework/Util/Filesystem/DirSetupUtil.php index 4207a2c8c..9f0036629 100644 --- a/src/Magento/FunctionalTestingFramework/Util/Filesystem/DirSetupUtil.php +++ b/src/Magento/FunctionalTestingFramework/Util/Filesystem/DirSetupUtil.php @@ -26,17 +26,19 @@ class DirSetupUtil */ public static function createGroupDir($fullPath) { + //prevent redundant calls to these directories + $sanitizedPath = rtrim($fullPath, DIRECTORY_SEPARATOR); // make sure we haven't already cleaned up this directory at any point before deletion - if (in_array($fullPath, self::$DIR_CONTEXT)) { + if (in_array($sanitizedPath, self::$DIR_CONTEXT)) { return; } - if (file_exists($fullPath)) { - self::rmDirRecursive($fullPath); + if (file_exists($sanitizedPath)) { + self::rmDirRecursive($sanitizedPath); } - mkdir($fullPath, 0777, true); - self::$DIR_CONTEXT[] = $fullPath; + mkdir($sanitizedPath, 0777, true); + self::$DIR_CONTEXT[] = $sanitizedPath; } /** From 9e1cc701cfa548f7c472531df91e03c0966fe2e5 Mon Sep 17 00:00:00 2001 From: Alexander Calandra Date: Thu, 26 Apr 2018 16:12:10 -0500 Subject: [PATCH 007/111] MQE-982: Verification Test around Suite Generation - Added CreateData and DeleteData Actions to suite verification test --- dev/_suite/functionalSuiteHooks.xml | 4 ++++ dev/tests/verification/Resources/functionalSuiteHooks.txt | 6 ++++++ 2 files changed, 10 insertions(+) diff --git a/dev/_suite/functionalSuiteHooks.xml b/dev/_suite/functionalSuiteHooks.xml index 1efa9c653..620a11fcd 100644 --- a/dev/_suite/functionalSuiteHooks.xml +++ b/dev/_suite/functionalSuiteHooks.xml @@ -13,9 +13,13 @@ + + + + diff --git a/dev/tests/verification/Resources/functionalSuiteHooks.txt b/dev/tests/verification/Resources/functionalSuiteHooks.txt index f9aea6ef4..e580f72e0 100644 --- a/dev/tests/verification/Resources/functionalSuiteHooks.txt +++ b/dev/tests/verification/Resources/functionalSuiteHooks.txt @@ -22,6 +22,7 @@ class functionalSuiteHooks extends \Codeception\GroupObject private $currentTestRun = 0; private static $HOOK_EXECUTION_INIT = "\n/******** Beginning execution of functionalSuiteHooks suite %s block ********/\n"; private static $HOOK_EXECUTION_END = "\n/******** Execution of functionalSuiteHooks suite %s block complete ********/\n"; + private $create; public function _before(\Codeception\Event\TestEvent $e) { @@ -53,6 +54,10 @@ class functionalSuiteHooks extends \Codeception\GroupObject // initialize the webdriver session $webDriver->_initializeSession(); $webDriver->amOnPage("some.url"); + $createFields['someKey'] = ; + $createThis = DataObjectHandler::getInstance()->getObject("createThis"); + $this->create = new DataPersistenceHandler($createThis, [], $createFields); + $this->create->createEntity(); // reset configuration and close session $this->getModule('\Magento\FunctionalTestingFramework\Module\MagentoWebDriver')->_resetConfig(); @@ -89,6 +94,7 @@ class functionalSuiteHooks extends \Codeception\GroupObject // initialize the webdriver session $webDriver->_initializeSession(); $webDriver->amOnPage("some.url"); + $webDriver->deleteEntityByUrl("deleteThis"); // reset configuration and close session $this->getModule('\Magento\FunctionalTestingFramework\Module\MagentoWebDriver')->_resetConfig(); From dfc9d85afc35b466425cc971f4a50586fc38000b Mon Sep 17 00:00:00 2001 From: Alexander Calandra Date: Fri, 27 Apr 2018 10:55:56 -0500 Subject: [PATCH 008/111] MQE-983: Unit Test for Module Resolver - Adding Unit Tests around Module Resolver --- .../Util/ModuleResolverTest.php | 126 ++++++++++++++++++ 1 file changed, 126 insertions(+) create mode 100644 dev/tests/unit/Magento/FunctionalTestFramework/Util/ModuleResolverTest.php diff --git a/dev/tests/unit/Magento/FunctionalTestFramework/Util/ModuleResolverTest.php b/dev/tests/unit/Magento/FunctionalTestFramework/Util/ModuleResolverTest.php new file mode 100644 index 000000000..559dc96c2 --- /dev/null +++ b/dev/tests/unit/Magento/FunctionalTestFramework/Util/ModuleResolverTest.php @@ -0,0 +1,126 @@ +setMockResolverClass(); + $resolver = ModuleResolver::getInstance(); + $this->setMockResolverProperties($resolver, ["example/paths"]); + $this->assertEquals(["example/paths"], $resolver->getModulesPath()); + } + + /** + * Validate paths are aggregated correctly + * @throws \Exception + */ + public function testGetModulePathsAggregate() + { + $this->setMockResolverClass(false, ["Magento_TestModule"]); + $resolver = ModuleResolver::getInstance(); + $this->setMockResolverProperties($resolver, null, null); + $this->assertEquals( + ['/git/magento2-functional-testing-framework/dev/tests/verification/TestModule'], + $resolver->getModulesPath() + ); + } + + /** + * Validate blacklisted modules are removed + * @throws \Exception + */ + public function testGetModulePathsBlacklist() + { + $this->setMockResolverClass(false, ["Magento_TestModule"]); + $resolver = ModuleResolver::getInstance(); + $this->setMockResolverProperties($resolver, null, null, ["TestModule"]); + $this->assertEquals([], $resolver->getModulesPath()); + } + + /** + * Validate that getEnabledModules returns correctly with no admin token + * @throws \Exception + */ + public function testGetModulePathsNoAdminToken() + { + $this->setMockResolverClass(false, null, ["example/paths"]); + $resolver = ModuleResolver::getInstance(); + $this->setMockResolverProperties($resolver, null, null); + $this->assertEquals(["example/paths"], $resolver->getModulesPath()); + } + + /** + * Function used to set mock for parser return and force init method to run between tests. + * + * @param string $mockToken + * @param array $mockModules + * @param string[] $mockCustomMethods + * @throws \Exception + */ + private function setMockResolverClass($mockToken = null, $mockModules = null, $mockCustomMethods = null) + { + $property = new \ReflectionProperty(ModuleResolver::class, 'instance'); + $property->setAccessible(true); + $property->setValue(null); + + $mockMethods = []; + if (isset($mockToken)) { + $mockMethods['getAdminToken'] = $mockToken; + } + if (isset($mockModules)) { + $mockMethods['getEnabledModules'] = $mockModules; + } + if (isset($mockCustomMethods)) { + $mockMethods['applyCustomModuleMethods'] = $mockCustomMethods; + } + $mockMethods['printMagentoVersionInfo'] = null; + + $mockResolver = AspectMock::double( + ModuleResolver::class, + $mockMethods + )->make(); + $instance = AspectMock::double(ObjectManager::class, ['create' => $mockResolver, 'get' => null])->make(); + // bypass the private constructor + AspectMock::double(ObjectManagerFactory::class, ['getObjectManager' => $instance]); + } + + /** + * Function used to set mock for Resolver properties + * + * @param ModuleResolver $instance + * @param array $mockPaths + * @param array $mockModules + * @throws \Exception + */ + private function setMockResolverProperties($instance, $mockPaths = null, $mockModules = null, $mockBlacklist = []) + { + $property = new \ReflectionProperty(ModuleResolver::class, 'enabledModulePaths'); + $property->setAccessible(true); + $property->setValue($instance, $mockPaths); + + $property = new \ReflectionProperty(ModuleResolver::class, 'enabledModules'); + $property->setAccessible(true); + $property->setValue($instance, $mockModules); + + $property = new \ReflectionProperty(ModuleResolver::class, 'moduleBlacklist'); + $property->setAccessible(true); + $property->setValue($instance, $mockBlacklist); + } +} From f02c4925b4850821a575ec2fd2328dc02848690f Mon Sep 17 00:00:00 2001 From: Alexander Calandra Date: Fri, 27 Apr 2018 11:28:56 -0500 Subject: [PATCH 009/111] MQE-982: Verification Test around Suite Generation - Added Cleanup for _generated before tests for this class start --- dev/tests/verification/Tests/SuiteGenerationTest.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/dev/tests/verification/Tests/SuiteGenerationTest.php b/dev/tests/verification/Tests/SuiteGenerationTest.php index 60298afdf..151065137 100644 --- a/dev/tests/verification/Tests/SuiteGenerationTest.php +++ b/dev/tests/verification/Tests/SuiteGenerationTest.php @@ -54,6 +54,11 @@ public static function setUpBeforeClass() unlink(self::getManifestFilePath()); } + // destroy _generated if it exists + if (file_exists(self::GENERATE_RESULT_DIR)) { + DirSetupUtil::rmdirRecursive(self::GENERATE_RESULT_DIR); + } + $configYml = fopen(self::CONFIG_YML_FILE, "w"); fclose($configYml); } From a044fe6df4da0131b587cf33d43e918f280519ea Mon Sep 17 00:00:00 2001 From: Alexander Calandra Date: Fri, 27 Apr 2018 13:28:10 -0500 Subject: [PATCH 010/111] MQE-983: Unit Test for Module Resolver - Changed Pathing to Relative for path check --- .../Magento/FunctionalTestFramework/Util/ModuleResolverTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dev/tests/unit/Magento/FunctionalTestFramework/Util/ModuleResolverTest.php b/dev/tests/unit/Magento/FunctionalTestFramework/Util/ModuleResolverTest.php index 559dc96c2..b70b23f28 100644 --- a/dev/tests/unit/Magento/FunctionalTestFramework/Util/ModuleResolverTest.php +++ b/dev/tests/unit/Magento/FunctionalTestFramework/Util/ModuleResolverTest.php @@ -37,7 +37,7 @@ public function testGetModulePathsAggregate() $resolver = ModuleResolver::getInstance(); $this->setMockResolverProperties($resolver, null, null); $this->assertEquals( - ['/git/magento2-functional-testing-framework/dev/tests/verification/TestModule'], + [TESTS_BP . DIRECTORY_SEPARATOR . 'verification' . DIRECTORY_SEPARATOR . 'TestModule'], $resolver->getModulesPath() ); } From 388c144c7ed1816374809fb92673b2da303a7c29 Mon Sep 17 00:00:00 2001 From: Alexander Calandra Date: Fri, 27 Apr 2018 13:47:39 -0500 Subject: [PATCH 011/111] MQE-982: Verification Test around Suite Generation - Changed assert to check for subset information --- dev/tests/verification/Tests/SuiteGenerationTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dev/tests/verification/Tests/SuiteGenerationTest.php b/dev/tests/verification/Tests/SuiteGenerationTest.php index 151065137..010f7e5df 100644 --- a/dev/tests/verification/Tests/SuiteGenerationTest.php +++ b/dev/tests/verification/Tests/SuiteGenerationTest.php @@ -136,7 +136,7 @@ public function testSuiteGenerationParallel() // Validate Yaml file updated $yml = Yaml::parse(file_get_contents(self::CONFIG_YML_FILE)); - $this->assertEquals($expectedGroups, array_keys($yml['groups'])); + $this->assertArraySubset(array_keys($yml['groups']), $expectedGroups); foreach ($expectedGroups as $expectedFolder) { $suiteResultBaseDir = self::GENERATE_RESULT_DIR . From 5e69b81133f4df549b90f550d447afca6373dbae Mon Sep 17 00:00:00 2001 From: Alexander Calandra Date: Fri, 27 Apr 2018 13:58:37 -0500 Subject: [PATCH 012/111] MQE-982: Verification Test around Suite Generation - Using array intersect instead of subset to avoid indexing issues --- dev/tests/verification/Tests/SuiteGenerationTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dev/tests/verification/Tests/SuiteGenerationTest.php b/dev/tests/verification/Tests/SuiteGenerationTest.php index 010f7e5df..238fc2a7c 100644 --- a/dev/tests/verification/Tests/SuiteGenerationTest.php +++ b/dev/tests/verification/Tests/SuiteGenerationTest.php @@ -136,7 +136,7 @@ public function testSuiteGenerationParallel() // Validate Yaml file updated $yml = Yaml::parse(file_get_contents(self::CONFIG_YML_FILE)); - $this->assertArraySubset(array_keys($yml['groups']), $expectedGroups); + $this->assertEquals(array_intersect($expectedGroups, array_keys($yml['groups'])), $expectedGroups); foreach ($expectedGroups as $expectedFolder) { $suiteResultBaseDir = self::GENERATE_RESULT_DIR . From f0201e532b936b30d1d99e79a5965067aee7e2e3 Mon Sep 17 00:00:00 2001 From: KevinBKozan Date: Fri, 27 Apr 2018 14:13:34 -0500 Subject: [PATCH 013/111] MQE-980: [Annotations] tag defaults to module - CR Fixes --- .../Test/Util/TestObjectExtractor.php | 22 +++++-------------- 1 file changed, 6 insertions(+), 16 deletions(-) diff --git a/src/Magento/FunctionalTestingFramework/Test/Util/TestObjectExtractor.php b/src/Magento/FunctionalTestingFramework/Test/Util/TestObjectExtractor.php index 7fa21c085..27d977ba0 100644 --- a/src/Magento/FunctionalTestingFramework/Test/Util/TestObjectExtractor.php +++ b/src/Magento/FunctionalTestingFramework/Test/Util/TestObjectExtractor.php @@ -137,22 +137,12 @@ private function extractModuleName($path) if ($path === "") { return "NO MODULE DETECTED"; } - $positions = []; - $lastPos = 0; - while (($lastPos = strpos($path, DIRECTORY_SEPARATOR, $lastPos))!== false) { - $positions[] = $lastPos; - $lastPos = $lastPos + strlen(DIRECTORY_SEPARATOR); + $paths = explode(DIRECTORY_SEPARATOR, $path); + if ($paths[count($paths)-3] == "Mftf") { + // app/code/Magento/[Analytics]/Test/Mftf/Test/SomeText.xml + return $paths[count($paths)-5]; } - $slashBeforeModule = $positions[count($positions)-3]; - $slashAfterModule = $positions[count($positions)-2]; - $output = substr($path, $slashBeforeModule+1, $slashAfterModule-$slashBeforeModule-1); - - //Check if file was actually from app/code or vendor - if ($output === "Mftf") { - $slashBeforeModule = $positions[count($positions)-5]; - $slashAfterModule = $positions[count($positions)-4]; - $output = substr($path, $slashBeforeModule+1, $slashAfterModule-$slashBeforeModule-1); - } - return $output; + // dev/tests/acceptance/tests/functional/Magento/FunctionalTest/[Analytics]/Test/SomeText.xml + return $paths[count($paths)-3]; } } From f52a4082c78ea8668687f11c3f38a98a4ac6a207 Mon Sep 17 00:00:00 2001 From: Grigoruta Cristian Date: Sun, 29 Apr 2018 22:03:51 +0300 Subject: [PATCH 014/111] Changed the location of the rewrite --- .../Module/MagentoWebDriver.php | 13 +++++++++++++ .../Util/TestGenerator.php | 15 --------------- 2 files changed, 13 insertions(+), 15 deletions(-) diff --git a/src/Magento/FunctionalTestingFramework/Module/MagentoWebDriver.php b/src/Magento/FunctionalTestingFramework/Module/MagentoWebDriver.php index 606437cd6..9ba822db7 100644 --- a/src/Magento/FunctionalTestingFramework/Module/MagentoWebDriver.php +++ b/src/Magento/FunctionalTestingFramework/Module/MagentoWebDriver.php @@ -554,4 +554,17 @@ public function saveScreenshot() $this->_saveScreenshot($this->pngReport = $outputDir . mb_strcut($filename, 0, 245, 'utf-8') . '.fail.png'); $this->_savePageSource($this->htmlReport = $outputDir . mb_strcut($filename, 0, 244, 'utf-8') . '.fail.html'); } + + /** + * Go to a page and wait for ajax requests to finish + * + * @param string $page + * @throws \Exception + * @return void + */ + public function amOnPage($page) + { + parent::amOnPage($page) + $this->waitForPageLoad(); + } } diff --git a/src/Magento/FunctionalTestingFramework/Util/TestGenerator.php b/src/Magento/FunctionalTestingFramework/Util/TestGenerator.php index ea599b3ee..07f069c10 100644 --- a/src/Magento/FunctionalTestingFramework/Util/TestGenerator.php +++ b/src/Magento/FunctionalTestingFramework/Util/TestGenerator.php @@ -1866,19 +1866,4 @@ private function printRuleErrorToConsole($key, $tagName, $attributes) $message .= '" can be use for action "' . $tagName . "\".\n"; print $message; } - - /** - * Go to a page and wait for ajax requests to finish - * - * @param string $page - * @throws \Exception - * @return void - */ - public function amOnPage($page) - { - $url = Uri::appendPath($this->config['url'], $page); - $this->debugSection('GET', $url); - $this->webDriver->get($url); - $this->waitForPageLoad(); - } } From b3cff790967f3870a6319707793ff9ea4048d8c2 Mon Sep 17 00:00:00 2001 From: Grigoruta Cristian Date: Mon, 30 Apr 2018 03:08:12 +0300 Subject: [PATCH 015/111] Fixed error --- .../FunctionalTestingFramework/Module/MagentoWebDriver.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Magento/FunctionalTestingFramework/Module/MagentoWebDriver.php b/src/Magento/FunctionalTestingFramework/Module/MagentoWebDriver.php index b6d350dcd..a1815aa70 100644 --- a/src/Magento/FunctionalTestingFramework/Module/MagentoWebDriver.php +++ b/src/Magento/FunctionalTestingFramework/Module/MagentoWebDriver.php @@ -609,7 +609,7 @@ public function saveScreenshot() */ public function amOnPage($page) { - parent::amOnPage($page) + parent::amOnPage($page); $this->waitForPageLoad(); } } From 362f06cc1f9d5c3ae6447877b158afd38f5e8bac Mon Sep 17 00:00:00 2001 From: Alexander Calandra Date: Mon, 30 Apr 2018 15:53:42 -0500 Subject: [PATCH 016/111] MQE-982: Verification Test around Suite Generation - Moved _suite to verification - Removed check to discard manifest.txt - Updated funcitonalSuiteHooks and corresponding resource file - Added mock to bootstrap to set file paths --- dev/tests/_bootstrap.php | 21 +++++++++++++++++++ .../Resources/functionalSuiteHooks.txt | 2 +- .../Tests/SuiteGenerationTest.php | 11 +++++----- .../verification}/_suite/functionalSuite.xml | 2 +- .../_suite/functionalSuiteHooks.xml | 4 ++-- 5 files changed, 30 insertions(+), 10 deletions(-) rename dev/{ => tests/verification}/_suite/functionalSuite.xml (89%) rename dev/{ => tests/verification}/_suite/functionalSuiteHooks.xml (80%) diff --git a/dev/tests/_bootstrap.php b/dev/tests/_bootstrap.php index 655e33910..e5e300ba8 100644 --- a/dev/tests/_bootstrap.php +++ b/dev/tests/_bootstrap.php @@ -62,6 +62,27 @@ require($unitUtilFile); } + +// Mocks suite files location getter return to get files in verification/_suite Directory +// This mocks the paths of the suite files but still parses the xml files +$suiteDirectory = TESTS_BP . DIRECTORY_SEPARATOR . "verification" . DIRECTORY_SEPARATOR . "_suite"; + +$paths = [ + $suiteDirectory . DIRECTORY_SEPARATOR . 'functionalSuite.xml', + $suiteDirectory . DIRECTORY_SEPARATOR . 'functionalSuiteHooks.xml' +]; + +// create and return the iterator for these file paths +$iterator = new Magento\FunctionalTestingFramework\Util\Iterator\File($paths); +try { + AspectMock\Test::double( + Magento\FunctionalTestingFramework\Config\FileResolver\Root::class, + ['get' => $iterator] + )->make(); +} catch (Exception $e) { + echo "Suite directory not mocked."; +} + function sortInterfaces($files) { $bottom = []; diff --git a/dev/tests/verification/Resources/functionalSuiteHooks.txt b/dev/tests/verification/Resources/functionalSuiteHooks.txt index e580f72e0..594cbc383 100644 --- a/dev/tests/verification/Resources/functionalSuiteHooks.txt +++ b/dev/tests/verification/Resources/functionalSuiteHooks.txt @@ -54,7 +54,7 @@ class functionalSuiteHooks extends \Codeception\GroupObject // initialize the webdriver session $webDriver->_initializeSession(); $webDriver->amOnPage("some.url"); - $createFields['someKey'] = ; + $createFields['someKey'] = "dataHere"; $createThis = DataObjectHandler::getInstance()->getObject("createThis"); $this->create = new DataPersistenceHandler($createThis, [], $createFields); $this->create->createEntity(); diff --git a/dev/tests/verification/Tests/SuiteGenerationTest.php b/dev/tests/verification/Tests/SuiteGenerationTest.php index 238fc2a7c..585c1fa86 100644 --- a/dev/tests/verification/Tests/SuiteGenerationTest.php +++ b/dev/tests/verification/Tests/SuiteGenerationTest.php @@ -6,9 +6,12 @@ namespace tests\verification\Tests; +use AspectMock\Test as AspectMock; use Magento\Framework\Module\Dir; +use Magento\FunctionalTestingFramework\Config\FileResolver\Root; use Magento\FunctionalTestingFramework\Suite\SuiteGenerator; use Magento\FunctionalTestingFramework\Util\Filesystem\DirSetupUtil; +use Magento\FunctionalTestingFramework\Util\Iterator\File; use Magento\FunctionalTestingFramework\Util\Manifest\ParallelTestManifest; use Magento\FunctionalTestingFramework\Util\Manifest\TestManifestFactory; use Symfony\Component\Yaml\Yaml; @@ -16,6 +19,7 @@ class SuiteGenerationTest extends MftfTestCase { + const TESTS_BP = DIRECTORY_SEPARATOR; const RESOURCES_DIR = TESTS_BP . DIRECTORY_SEPARATOR . 'verification' . DIRECTORY_SEPARATOR . 'Resources'; const CONFIG_YML_FILE = FW_BP . DIRECTORY_SEPARATOR . SuiteGenerator::YAML_CODECEPTION_CONFIG_FILENAME; const GENERATE_RESULT_DIR = TESTS_BP . @@ -49,11 +53,6 @@ public static function setUpBeforeClass() return; } - // destroy manifest file if it exists - if (file_exists(self::getManifestFilePath())) { - unlink(self::getManifestFilePath()); - } - // destroy _generated if it exists if (file_exists(self::GENERATE_RESULT_DIR)) { DirSetupUtil::rmdirRecursive(self::GENERATE_RESULT_DIR); @@ -68,7 +67,7 @@ public static function setUpBeforeClass() */ public function testSuiteGeneration1() { - $groupName = 'functionalSuite1'; + $groupName = 'functionalSuite1'; $expectedContents = [ 'additionalTestCest.php', diff --git a/dev/_suite/functionalSuite.xml b/dev/tests/verification/_suite/functionalSuite.xml similarity index 89% rename from dev/_suite/functionalSuite.xml rename to dev/tests/verification/_suite/functionalSuite.xml index 1a7326664..439445c24 100644 --- a/dev/_suite/functionalSuite.xml +++ b/dev/tests/verification/_suite/functionalSuite.xml @@ -6,7 +6,7 @@ */ --> - + diff --git a/dev/_suite/functionalSuiteHooks.xml b/dev/tests/verification/_suite/functionalSuiteHooks.xml similarity index 80% rename from dev/_suite/functionalSuiteHooks.xml rename to dev/tests/verification/_suite/functionalSuiteHooks.xml index 620a11fcd..ad6628173 100644 --- a/dev/_suite/functionalSuiteHooks.xml +++ b/dev/tests/verification/_suite/functionalSuiteHooks.xml @@ -6,7 +6,7 @@ */ --> - + @@ -14,7 +14,7 @@ - + dataHere From 79243f5639a9989d4bf0c9e844f68fb13c06508c Mon Sep 17 00:00:00 2001 From: Alexander Calandra Date: Mon, 30 Apr 2018 16:03:39 -0500 Subject: [PATCH 017/111] MQE-985: Verification Test around Suite Generation - Removed unused references --- dev/tests/verification/Tests/SuiteGenerationTest.php | 5 ----- 1 file changed, 5 deletions(-) diff --git a/dev/tests/verification/Tests/SuiteGenerationTest.php b/dev/tests/verification/Tests/SuiteGenerationTest.php index 585c1fa86..5f2d3a6d2 100644 --- a/dev/tests/verification/Tests/SuiteGenerationTest.php +++ b/dev/tests/verification/Tests/SuiteGenerationTest.php @@ -6,12 +6,8 @@ namespace tests\verification\Tests; -use AspectMock\Test as AspectMock; -use Magento\Framework\Module\Dir; -use Magento\FunctionalTestingFramework\Config\FileResolver\Root; use Magento\FunctionalTestingFramework\Suite\SuiteGenerator; use Magento\FunctionalTestingFramework\Util\Filesystem\DirSetupUtil; -use Magento\FunctionalTestingFramework\Util\Iterator\File; use Magento\FunctionalTestingFramework\Util\Manifest\ParallelTestManifest; use Magento\FunctionalTestingFramework\Util\Manifest\TestManifestFactory; use Symfony\Component\Yaml\Yaml; @@ -19,7 +15,6 @@ class SuiteGenerationTest extends MftfTestCase { - const TESTS_BP = DIRECTORY_SEPARATOR; const RESOURCES_DIR = TESTS_BP . DIRECTORY_SEPARATOR . 'verification' . DIRECTORY_SEPARATOR . 'Resources'; const CONFIG_YML_FILE = FW_BP . DIRECTORY_SEPARATOR . SuiteGenerator::YAML_CODECEPTION_CONFIG_FILENAME; const GENERATE_RESULT_DIR = TESTS_BP . From b938edea27a292e06306ba97d615c89d78310a80 Mon Sep 17 00:00:00 2001 From: Alexander Calandra Date: Tue, 1 May 2018 13:36:21 -0500 Subject: [PATCH 018/111] MQE-983: Unit Test for Module Resolver - Added explict check for location paths - Added Custom Module Check - Updated Aspect Mock version - Updated goaop version - Added wrapper for glob() --- composer.json | 4 +- composer.lock | 128 ++++++++++++++---- dev/tests/.cache/.gitignore | 4 + dev/tests/_bootstrap.php | 9 +- .../Util/ModuleResolverTest.php | 103 ++++++++++++-- .../Util/ModuleResolver.php | 14 +- 6 files changed, 222 insertions(+), 40 deletions(-) create mode 100644 dev/tests/.cache/.gitignore diff --git a/composer.json b/composer.json index b59d02dce..83d7ff7f5 100755 --- a/composer.json +++ b/composer.json @@ -24,8 +24,8 @@ "squizlabs/php_codesniffer": "1.5.3", "sebastian/phpcpd": "~3.0", "brainmaestro/composer-git-hooks": "^2.3", - "codeception/aspect-mock": "^2.0", - "goaop/framework": "2.1.2", + "codeception/aspect-mock": "^3.0", + "goaop/framework": "2.2.0", "codacy/coverage": "^1.4", "phpmd/phpmd": "^2.6.0", "rregeer/phpunit-coverage-check": "^0.1.4", diff --git a/composer.lock b/composer.lock index de8728da1..5f9c19ce1 100644 --- a/composer.lock +++ b/composer.lock @@ -1,10 +1,10 @@ { "_readme": [ "This file locks the dependencies of your project to a known state", - "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", + "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "d394ebb149854c00f790da70a8f0a3f3", + "content-hash": "08b7692ea8194663fc705a21221dda73", "packages": [ { "name": "allure-framework/allure-codeception", @@ -2472,16 +2472,16 @@ }, { "name": "phpunit/phpunit", - "version": "6.5.7", + "version": "6.5.8", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "6bd77b57707c236833d2b57b968e403df060c9d9" + "reference": "4f21a3c6b97c42952fd5c2837bb354ec0199b97b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/6bd77b57707c236833d2b57b968e403df060c9d9", - "reference": "6bd77b57707c236833d2b57b968e403df060c9d9", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/4f21a3c6b97c42952fd5c2837bb354ec0199b97b", + "reference": "4f21a3c6b97c42952fd5c2837bb354ec0199b97b", "shasum": "" }, "require": { @@ -2552,7 +2552,7 @@ "testing", "xunit" ], - "time": "2018-02-26T07:01:09+00:00" + "time": "2018-04-10T11:38:34+00:00" }, { "name": "phpunit/phpunit-mock-objects", @@ -4365,25 +4365,26 @@ }, { "name": "codeception/aspect-mock", - "version": "2.1.1", + "version": "3.0.0", "source": { "type": "git", "url": "https://github.com/Codeception/AspectMock.git", - "reference": "bf3c000599c0dc75ecb52e19dee2b8ed294cf7ba" + "reference": "793aad0a4e9f238ffc5a107337f57c98aa29d2cf" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Codeception/AspectMock/zipball/bf3c000599c0dc75ecb52e19dee2b8ed294cf7ba", - "reference": "bf3c000599c0dc75ecb52e19dee2b8ed294cf7ba", + "url": "https://api.github.com/repos/Codeception/AspectMock/zipball/793aad0a4e9f238ffc5a107337f57c98aa29d2cf", + "reference": "793aad0a4e9f238ffc5a107337f57c98aa29d2cf", "shasum": "" }, "require": { - "goaop/framework": "^2.0.0", - "php": ">=5.6.0", - "symfony/finder": "~2.4|~3.0" + "goaop/framework": "^2.2.0", + "php": ">=7.0.0", + "phpunit/phpunit": "> 6.0.0", + "symfony/finder": "~2.4|~3.0|~4.0" }, "require-dev": { - "codeception/base": "~2.1", + "codeception/base": "^2.4", "codeception/specify": "~0.3", "codeception/verify": "~0.2" }, @@ -4404,7 +4405,81 @@ } ], "description": "Experimental Mocking Framework powered by Aspects", - "time": "2017-10-24T10:20:17+00:00" + "time": "2018-03-20T08:44:00+00:00" + }, + { + "name": "doctrine/cache", + "version": "v1.7.1", + "source": { + "type": "git", + "url": "https://github.com/doctrine/cache.git", + "reference": "b3217d58609e9c8e661cd41357a54d926c4a2a1a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/cache/zipball/b3217d58609e9c8e661cd41357a54d926c4a2a1a", + "reference": "b3217d58609e9c8e661cd41357a54d926c4a2a1a", + "shasum": "" + }, + "require": { + "php": "~7.1" + }, + "conflict": { + "doctrine/common": ">2.2,<2.4" + }, + "require-dev": { + "alcaeus/mongo-php-adapter": "^1.1", + "mongodb/mongodb": "^1.1", + "phpunit/phpunit": "^5.7", + "predis/predis": "~1.0" + }, + "suggest": { + "alcaeus/mongo-php-adapter": "Required to use legacy MongoDB driver" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.7.x-dev" + } + }, + "autoload": { + "psr-4": { + "Doctrine\\Common\\Cache\\": "lib/Doctrine/Common/Cache" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Roman Borschel", + "email": "roman@code-factory.org" + }, + { + "name": "Benjamin Eberlei", + "email": "kontakt@beberlei.de" + }, + { + "name": "Guilherme Blanco", + "email": "guilhermeblanco@gmail.com" + }, + { + "name": "Jonathan Wage", + "email": "jonwage@gmail.com" + }, + { + "name": "Johannes Schmitt", + "email": "schmittjoh@gmail.com" + } + ], + "description": "Caching library offering an object-oriented API for many cache backends", + "homepage": "http://www.doctrine-project.org", + "keywords": [ + "cache", + "caching" + ], + "time": "2017-08-25T07:02:50+00:00" }, { "name": "gitonomy/gitlib", @@ -4464,29 +4539,32 @@ }, { "name": "goaop/framework", - "version": "2.1.2", + "version": "2.2.0", "source": { "type": "git", "url": "https://github.com/goaop/framework.git", - "reference": "6e2a0fe13c1943db02a67588cfd27692bddaffa5" + "reference": "152abbffffcba72d2d159b892deb40b0829d0f28" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/goaop/framework/zipball/6e2a0fe13c1943db02a67588cfd27692bddaffa5", - "reference": "6e2a0fe13c1943db02a67588cfd27692bddaffa5", + "url": "https://api.github.com/repos/goaop/framework/zipball/152abbffffcba72d2d159b892deb40b0829d0f28", + "reference": "152abbffffcba72d2d159b892deb40b0829d0f28", "shasum": "" }, "require": { - "doctrine/annotations": "~1.0", - "goaop/parser-reflection": "~1.2", + "doctrine/annotations": "^1.2.3", + "doctrine/cache": "^1.5", + "goaop/parser-reflection": "~1.4", "jakubledl/dissect": "~1.0", "php": ">=5.6.0" }, "require-dev": { "adlawson/vfs": "^0.12", "doctrine/orm": "^2.5", - "phpunit/phpunit": "^4.8", - "symfony/console": "^2.7|^3.0" + "phpunit/phpunit": "^5.7", + "symfony/console": "^2.7|^3.0", + "symfony/filesystem": "^3.3", + "symfony/process": "^3.3" }, "suggest": { "symfony/console": "Enables the usage of the command-line tool." @@ -4523,7 +4601,7 @@ "library", "php" ], - "time": "2017-07-12T11:46:25+00:00" + "time": "2018-01-05T23:07:51+00:00" }, { "name": "goaop/parser-reflection", diff --git a/dev/tests/.cache/.gitignore b/dev/tests/.cache/.gitignore new file mode 100644 index 000000000..86d0cb272 --- /dev/null +++ b/dev/tests/.cache/.gitignore @@ -0,0 +1,4 @@ +# Ignore everything in this directory +* +# Except this file +!.gitignore \ No newline at end of file diff --git a/dev/tests/_bootstrap.php b/dev/tests/_bootstrap.php index 655e33910..b8d2de033 100644 --- a/dev/tests/_bootstrap.php +++ b/dev/tests/_bootstrap.php @@ -13,7 +13,14 @@ $kernel = \AspectMock\Kernel::getInstance(); $kernel->init([ 'debug' => true, - 'includePaths' => [PROJECT_ROOT . '/src'] + 'includePaths' => [PROJECT_ROOT . '/src'], + 'cacheDir' => PROJECT_ROOT . + DIRECTORY_SEPARATOR . + 'dev' . + DIRECTORY_SEPARATOR . + 'tests' . + DIRECTORY_SEPARATOR . + '.cache' ]); // set mftf appplication context diff --git a/dev/tests/unit/Magento/FunctionalTestFramework/Util/ModuleResolverTest.php b/dev/tests/unit/Magento/FunctionalTestFramework/Util/ModuleResolverTest.php index b70b23f28..f80ce0223 100644 --- a/dev/tests/unit/Magento/FunctionalTestFramework/Util/ModuleResolverTest.php +++ b/dev/tests/unit/Magento/FunctionalTestFramework/Util/ModuleResolverTest.php @@ -6,6 +6,7 @@ namespace tests\unit\Magento\FunctionalTestFramework\Test\Util; +use AspectMock\Proxy\Verifier; use AspectMock\Test as AspectMock; use Magento\FunctionalTestingFramework\ObjectManager; @@ -15,6 +16,14 @@ class ModuleResolverTest extends TestCase { + /** + * remove all registered test doubles + */ + protected function tearDown() + { + AspectMock::clean(); + } + /** * Validate that Paths that are already set are returned * @throws \Exception @@ -33,25 +42,72 @@ public function testGetModulePathsAlreadySet() */ public function testGetModulePathsAggregate() { - $this->setMockResolverClass(false, ["Magento_TestModule"]); + $this->setMockResolverClass(false, null, null, null, ['example/paths']); $resolver = ModuleResolver::getInstance(); $this->setMockResolverProperties($resolver, null, null); $this->assertEquals( - [TESTS_BP . DIRECTORY_SEPARATOR . 'verification' . DIRECTORY_SEPARATOR . 'TestModule'], + ['example/paths', 'example/paths', 'example/paths'], $resolver->getModulesPath() ); } + /** + * Validate correct path locations are fed into globRelevantPaths + * @throws \Exception + */ + public function testGetModulePathsLocations() + { + $mockResolver = $this->setMockResolverClass(false, null, null, null, ['example/paths']); + $resolver = ModuleResolver::getInstance(); + $this->setMockResolverProperties($resolver, null, null); + $this->assertEquals( + ['example/paths', 'example/paths', 'example/paths'], + $resolver->getModulesPath() + ); + + // Define the Module paths from app/code + $appCodePath = MAGENTO_BP + . DIRECTORY_SEPARATOR + . 'app' . DIRECTORY_SEPARATOR + . 'code' . DIRECTORY_SEPARATOR; + + // Define the Module paths from default TESTS_MODULE_PATH + $modulePath = defined('TESTS_MODULE_PATH') ? TESTS_MODULE_PATH : TESTS_BP; + + // Define the Module paths from vendor modules + $vendorCodePath = PROJECT_ROOT + . DIRECTORY_SEPARATOR + . 'vendor' . DIRECTORY_SEPARATOR; + + $mockResolver->verifyInvoked('globRelevantPaths', [$modulePath, '']); + $mockResolver->verifyInvoked('globRelevantPaths', [$appCodePath, '/Test/Mftf']); + $mockResolver->verifyInvoked('globRelevantPaths', [$vendorCodePath, '/Test/Mftf']); + } + + /** + * Validate custom modules are added + * @throws \Exception + */ + public function testGetCustomModulePath() + { + $this->setMockResolverClass(false, ["Magento_TestModule"], null, null, [], ['otherPath']); + $this->expectOutputString("Including module path: otherPath" . PHP_EOL); + $resolver = ModuleResolver::getInstance(); + $this->setMockResolverProperties($resolver, null, null, null); + $this->assertEquals(['otherPath'], $resolver->getModulesPath()); + } + /** * Validate blacklisted modules are removed * @throws \Exception */ public function testGetModulePathsBlacklist() { - $this->setMockResolverClass(false, ["Magento_TestModule"]); + $this->setMockResolverClass(false, ["Magento_TestModule"], null, null, ['somePath']); + $this->expectOutputString("Excluding module: 0" . PHP_EOL); $resolver = ModuleResolver::getInstance(); - $this->setMockResolverProperties($resolver, null, null, ["TestModule"]); - $this->assertEquals([], $resolver->getModulesPath()); + $this->setMockResolverProperties($resolver, null, null, ["somePath"]); + $this->assertEquals(['somePath', 'somePath'], $resolver->getModulesPath()); } /** @@ -60,7 +116,7 @@ public function testGetModulePathsBlacklist() */ public function testGetModulePathsNoAdminToken() { - $this->setMockResolverClass(false, null, ["example/paths"]); + $this->setMockResolverClass(false, null, ["example/paths"], []); $resolver = ModuleResolver::getInstance(); $this->setMockResolverProperties($resolver, null, null); $this->assertEquals(["example/paths"], $resolver->getModulesPath()); @@ -70,12 +126,22 @@ public function testGetModulePathsNoAdminToken() * Function used to set mock for parser return and force init method to run between tests. * * @param string $mockToken - * @param array $mockModules + * @param array $mockGetModules * @param string[] $mockCustomMethods + * @param string[] $mockGlob + * @param string[] $mockRelativePaths + * @param string[] $mockCustomModules * @throws \Exception + * @return Verifier ModuleResolver double */ - private function setMockResolverClass($mockToken = null, $mockModules = null, $mockCustomMethods = null) - { + private function setMockResolverClass( + $mockToken = null, + $mockGetModules = null, + $mockCustomMethods = null, + $mockGlob = null, + $mockRelativePaths = null, + $mockCustomModules = null + ) { $property = new \ReflectionProperty(ModuleResolver::class, 'instance'); $property->setAccessible(true); $property->setValue(null); @@ -85,20 +151,34 @@ private function setMockResolverClass($mockToken = null, $mockModules = null, $m $mockMethods['getAdminToken'] = $mockToken; } if (isset($mockModules)) { - $mockMethods['getEnabledModules'] = $mockModules; + $mockMethods['getEnabledModules'] = $mockGetModules; } if (isset($mockCustomMethods)) { $mockMethods['applyCustomModuleMethods'] = $mockCustomMethods; } + if (isset($mockGlob)) { + $mockMethods['globRelevantWrapper'] = $mockGlob; + } + if (isset($mockRelativePaths)) { + $mockMethods['globRelevantPaths'] = $mockRelativePaths; + } + if (isset($mockCustomModules)) { + $mockMethods['getCustomModulePaths'] = $mockCustomModules; + } $mockMethods['printMagentoVersionInfo'] = null; $mockResolver = AspectMock::double( ModuleResolver::class, $mockMethods + ); + $instance = AspectMock::double( + ObjectManager::class, + ['create' => $mockResolver->make(), 'get' => null] )->make(); - $instance = AspectMock::double(ObjectManager::class, ['create' => $mockResolver, 'get' => null])->make(); // bypass the private constructor AspectMock::double(ObjectManagerFactory::class, ['getObjectManager' => $instance]); + + return $mockResolver; } /** @@ -107,6 +187,7 @@ private function setMockResolverClass($mockToken = null, $mockModules = null, $m * @param ModuleResolver $instance * @param array $mockPaths * @param array $mockModules + * @param array $mockBlacklist * @throws \Exception */ private function setMockResolverProperties($instance, $mockPaths = null, $mockModules = null, $mockBlacklist = []) diff --git a/src/Magento/FunctionalTestingFramework/Util/ModuleResolver.php b/src/Magento/FunctionalTestingFramework/Util/ModuleResolver.php index f3a6eb6e6..43b472b4d 100644 --- a/src/Magento/FunctionalTestingFramework/Util/ModuleResolver.php +++ b/src/Magento/FunctionalTestingFramework/Util/ModuleResolver.php @@ -263,7 +263,7 @@ private function globRelevantPaths($testPath, $pattern) $relevantPaths = []; if (file_exists($testPath)) { - $relevantPaths = glob($testPath . '*/*' . $pattern); + $relevantPaths = $this->globRelevantWrapper($testPath, $pattern); } foreach ($relevantPaths as $codePath) { @@ -274,6 +274,18 @@ private function globRelevantPaths($testPath, $pattern) return $modulePaths; } + /** + * Glob wrapper for globRelevantPaths function + * + * @param string $testPath + * @param string $pattern + * @return array + */ + private static function globRelevantWrapper($testPath, $pattern) + { + return glob($testPath . '*/*' . $pattern); + } + /** * Takes a multidimensional array of module paths and flattens to return a one dimensional array of test paths * From 4aab74e805072359b25d55381144a3f48142637b Mon Sep 17 00:00:00 2001 From: Alexander Calandra Date: Tue, 1 May 2018 14:01:16 -0500 Subject: [PATCH 019/111] MQE-983: Unit Test for Module Resolver - Changed doctrine/cache to <1.7.0 for php 7.0 compatability --- composer.json | 1 + composer.lock | 26 +++++++++++--------------- 2 files changed, 12 insertions(+), 15 deletions(-) diff --git a/composer.json b/composer.json index 83d7ff7f5..d404a4fe8 100755 --- a/composer.json +++ b/composer.json @@ -24,6 +24,7 @@ "squizlabs/php_codesniffer": "1.5.3", "sebastian/phpcpd": "~3.0", "brainmaestro/composer-git-hooks": "^2.3", + "doctrine/cache": "<1.7.0", "codeception/aspect-mock": "^3.0", "goaop/framework": "2.2.0", "codacy/coverage": "^1.4", diff --git a/composer.lock b/composer.lock index 5f9c19ce1..8b13ba39a 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "08b7692ea8194663fc705a21221dda73", + "content-hash": "723c7b1ff62d50ff5e7d9aeaeb91b77c", "packages": [ { "name": "allure-framework/allure-codeception", @@ -4409,37 +4409,33 @@ }, { "name": "doctrine/cache", - "version": "v1.7.1", + "version": "v1.6.2", "source": { "type": "git", "url": "https://github.com/doctrine/cache.git", - "reference": "b3217d58609e9c8e661cd41357a54d926c4a2a1a" + "reference": "eb152c5100571c7a45470ff2a35095ab3f3b900b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/cache/zipball/b3217d58609e9c8e661cd41357a54d926c4a2a1a", - "reference": "b3217d58609e9c8e661cd41357a54d926c4a2a1a", + "url": "https://api.github.com/repos/doctrine/cache/zipball/eb152c5100571c7a45470ff2a35095ab3f3b900b", + "reference": "eb152c5100571c7a45470ff2a35095ab3f3b900b", "shasum": "" }, "require": { - "php": "~7.1" + "php": "~5.5|~7.0" }, "conflict": { "doctrine/common": ">2.2,<2.4" }, "require-dev": { - "alcaeus/mongo-php-adapter": "^1.1", - "mongodb/mongodb": "^1.1", - "phpunit/phpunit": "^5.7", - "predis/predis": "~1.0" - }, - "suggest": { - "alcaeus/mongo-php-adapter": "Required to use legacy MongoDB driver" + "phpunit/phpunit": "~4.8|~5.0", + "predis/predis": "~1.0", + "satooshi/php-coveralls": "~0.6" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.7.x-dev" + "dev-master": "1.6.x-dev" } }, "autoload": { @@ -4479,7 +4475,7 @@ "cache", "caching" ], - "time": "2017-08-25T07:02:50+00:00" + "time": "2017-07-22T12:49:21+00:00" }, { "name": "gitonomy/gitlib", From 9d7e4ceac9a7bcdb0b3bcb837cdd8a10c90e5180 Mon Sep 17 00:00:00 2001 From: KevinBKozan Date: Tue, 1 May 2018 14:25:31 -0500 Subject: [PATCH 020/111] MQE-812: Tests/Action Groups should infer order based on the top level argument - Test/Dom.php now checks for a test to have a before/after attribute, if found it appends the before/after to all child actions for merging down the line. --- .../Test/Config/Dom.php | 49 +++++++++++++++++++ .../Test/Util/ActionMergeUtil.php | 2 +- .../Test/Util/TestObjectExtractor.php | 6 ++- .../Test/etc/mergedTestSchema.xsd | 2 + 4 files changed, 56 insertions(+), 3 deletions(-) diff --git a/src/Magento/FunctionalTestingFramework/Test/Config/Dom.php b/src/Magento/FunctionalTestingFramework/Test/Config/Dom.php index 24710243f..3d8c2c4ba 100644 --- a/src/Magento/FunctionalTestingFramework/Test/Config/Dom.php +++ b/src/Magento/FunctionalTestingFramework/Test/Config/Dom.php @@ -17,6 +17,8 @@ class Dom extends \Magento\FunctionalTestingFramework\Config\Dom const TEST_META_FILENAME_ATTRIBUTE = 'filename'; const TEST_META_NAME_ATTRIBUTE = 'name'; const TEST_HOOK_NAMES = ["after", "before"]; + const TEST_MERGE_POINTER_BEFORE = "before"; + const TEST_MERGE_POINTER_AFTER = "after"; /** * TestDom constructor. @@ -63,6 +65,23 @@ public function initDom($xml, $filename = null, $exceptionCollector = null) /** @var \DOMElement $testNode */ $testNode->setAttribute(self::TEST_META_FILENAME_ATTRIBUTE, $filename); $this->validateDomStepKeys($testNode, $filename, 'Test', $exceptionCollector); + if ($testNode->getAttribute(self::TEST_MERGE_POINTER_AFTER) !== "") { + $this->appendMergePointerToActions( + $testNode, + self::TEST_MERGE_POINTER_AFTER, + $testNode->getAttribute(self::TEST_MERGE_POINTER_AFTER), + $filename, + $exceptionCollector + ); + } elseif ($testNode->getAttribute(self::TEST_MERGE_POINTER_BEFORE) !== "") { + $this->appendMergePointerToActions( + $testNode, + self::TEST_MERGE_POINTER_BEFORE, + $testNode->getAttribute(self::TEST_MERGE_POINTER_BEFORE), + $filename, + $exceptionCollector + ); + } } } @@ -83,6 +102,36 @@ public function merge($xml, $filename = null, $exceptionCollector = null) $this->mergeNode($dom->documentElement, ''); } + /** + * Parses DOM Structure's actions and appends a before/after attribute along with the parent's stepkey reference. + * + * @param \DOMElement $testNode + * @param string $pointerType + * @param string $pointerKey + * @param string $filename + * @param ExceptionCollector $exceptionCollector + * @return void + */ + protected function appendMergePointerToActions($testNode, $pointerType, $pointerKey, $filename, $exceptionCollector) + { + $childNodes = $testNode->childNodes; + for ($i = 0; $i < $childNodes->length; $i++) { + $currentNode = $childNodes->item($i); + if (!is_a($currentNode, \DOMElement::class)) { + continue; + } + if ($currentNode->hasAttribute('stepKey')) { + if ($currentNode->hasAttribute($pointerType) && $testNode->hasAttribute($pointerType)) { + $errorMsg = "Actions cannot have a before/after pointer if they are in a test that provides a merge pointer."; + $errorMsg .= "\n\tstepKey: {$currentNode->getAttribute('stepKey')}\tin file: {$filename}"; + $exceptionCollector->addError($filename, $errorMsg); + continue; + } + $currentNode->setAttribute($pointerType, $pointerKey); + } + } + } + /** * Parses an individual DOM structure for repeated stepKey attributes * diff --git a/src/Magento/FunctionalTestingFramework/Test/Util/ActionMergeUtil.php b/src/Magento/FunctionalTestingFramework/Test/Util/ActionMergeUtil.php index 8af5e3af0..51dea1893 100644 --- a/src/Magento/FunctionalTestingFramework/Test/Util/ActionMergeUtil.php +++ b/src/Magento/FunctionalTestingFramework/Test/Util/ActionMergeUtil.php @@ -18,7 +18,7 @@ class ActionMergeUtil { const STEP_MISSING_ERROR_MSG = "Merge Error - Step could not be found in either TestXML or DeltaXML. - \t%s = '%s'\tTestStep='%s'\tLinkedStep'%s'"; + \t%s: '%s'\tTestStep: '%s'\tLinkedStep: '%s'"; const WAIT_ATTR = 'timeout'; const WAIT_ACTION_NAME = 'waitForPageLoad'; diff --git a/src/Magento/FunctionalTestingFramework/Test/Util/TestObjectExtractor.php b/src/Magento/FunctionalTestingFramework/Test/Util/TestObjectExtractor.php index be4e3c97c..cfe603a5f 100644 --- a/src/Magento/FunctionalTestingFramework/Test/Util/TestObjectExtractor.php +++ b/src/Magento/FunctionalTestingFramework/Test/Util/TestObjectExtractor.php @@ -21,6 +21,8 @@ class TestObjectExtractor extends BaseObjectExtractor const TEST_BEFORE_HOOK = 'before'; const TEST_AFTER_HOOK = 'after'; const TEST_FAILED_HOOK = 'failed'; + const TEST_BEFORE_ATTRIBUTE = 'before'; + const TEST_AFTER_ATTRIBUTE = 'after'; /** * Action Object Extractor object @@ -85,7 +87,7 @@ public function extractTestData($testData) } // extract before - if (array_key_exists(self::TEST_BEFORE_HOOK, $testData)) { + if (array_key_exists(self::TEST_BEFORE_HOOK, $testData) && is_array($testData[self::TEST_BEFORE_HOOK])) { $testHooks[self::TEST_BEFORE_HOOK] = $this->testHookObjectExtractor->extractHook( $testData[self::NAME], 'before', @@ -93,7 +95,7 @@ public function extractTestData($testData) ); } - if (array_key_exists(self::TEST_AFTER_HOOK, $testData)) { + if (array_key_exists(self::TEST_AFTER_HOOK, $testData) && is_array($testData[self::TEST_AFTER_HOOK])) { // extract after $testHooks[self::TEST_AFTER_HOOK] = $this->testHookObjectExtractor->extractHook( $testData[self::NAME], diff --git a/src/Magento/FunctionalTestingFramework/Test/etc/mergedTestSchema.xsd b/src/Magento/FunctionalTestingFramework/Test/etc/mergedTestSchema.xsd index 012083ab8..125bd8baf 100644 --- a/src/Magento/FunctionalTestingFramework/Test/etc/mergedTestSchema.xsd +++ b/src/Magento/FunctionalTestingFramework/Test/etc/mergedTestSchema.xsd @@ -70,6 +70,8 @@ + + From 28566840cd3616763aeffab122d373cbe44fc3d0 Mon Sep 17 00:00:00 2001 From: aljcalandra Date: Tue, 1 May 2018 14:29:31 -0500 Subject: [PATCH 021/111] MQE-978: Create Test Coverage Around SuiteGenerator Class - Adding unit tests for generateSuite and generateAllSuites - Removed unused code from SuiteGenerator Class - Removing CLEARED_MANIFESTS reference - Adjusting comment text --- .../Suite/SuiteGeneratorTest.php | 149 ++++++++++++++++++ .../Suite/SuiteGenerator.php | 36 +---- 2 files changed, 150 insertions(+), 35 deletions(-) create mode 100644 dev/tests/unit/Magento/FunctionalTestFramework/Suite/SuiteGeneratorTest.php diff --git a/dev/tests/unit/Magento/FunctionalTestFramework/Suite/SuiteGeneratorTest.php b/dev/tests/unit/Magento/FunctionalTestFramework/Suite/SuiteGeneratorTest.php new file mode 100644 index 000000000..e7c607729 --- /dev/null +++ b/dev/tests/unit/Magento/FunctionalTestFramework/Suite/SuiteGeneratorTest.php @@ -0,0 +1,149 @@ +withName('basicTestSuite') + ->withAfterHook() + ->withBeforeHook() + ->includeTests(['simpleTest']) + ->includeGroups(['group1']) + ->build(); + + $testDataArrayBuilder = new TestDataArrayBuilder(); + $mockSimpleTest = $testDataArrayBuilder + ->withName('simpleTest') + ->withTestActions() + ->build(); + + $mockTestData = ['tests' => array_merge($mockSimpleTest)]; + $this->setMockTestAndSuiteParserOutput($mockTestData, $mockData); + + // parse and generate suite object with mocked data + $mockSuiteGenerator = SuiteGenerator::getInstance(); + $mockSuiteGenerator->generateSuite("basicTestSuite"); + + // assert that expected suite is generated + $this->expectOutputString("Suite basicTestSuite generated to _generated/basicTestSuite." . PHP_EOL); + } + + /** + * Tests generating all suites given a set of parsed test data + * @throws \Exception + */ + public function testGenerateAllSuites() + { + $suiteDataArrayBuilder = new SuiteDataArrayBuilder(); + $mockData = $suiteDataArrayBuilder + ->withName('basicTestSuite') + ->withAfterHook() + ->withBeforeHook() + ->includeTests(['simpleTest']) + ->includeGroups(['group1']) + ->build(); + + $testDataArrayBuilder = new TestDataArrayBuilder(); + $mockSimpleTest = $testDataArrayBuilder + ->withName('simpleTest') + ->withTestActions() + ->build(); + + $mockTestData = ['tests' => array_merge($mockSimpleTest)]; + $this->setMockTestAndSuiteParserOutput($mockTestData, $mockData); + + // parse and retrieve suite object with mocked data + $exampleTestManifest = new DefaultTestManifest([], "sample/path"); + $mockSuiteGenerator = SuiteGenerator::getInstance(); + $mockSuiteGenerator->generateAllSuites($exampleTestManifest); + + // assert that expected suites are generated + $this->expectOutputString("Suite basicTestSuite generated to _generated/basicTestSuite." . PHP_EOL); + } + + /** + * Function used to set mock for parser return and force init method to run between tests. + * + * @param array $testData + * @throws \Exception + */ + private function setMockTestAndSuiteParserOutput($testData, $suiteData) + { + $property = new \ReflectionProperty(SuiteGenerator::class, 'SUITE_GENERATOR_INSTANCE'); + $property->setAccessible(true); + $property->setValue(null); + + // clear test object handler value to inject parsed content + $property = new \ReflectionProperty(TestObjectHandler::class, 'testObjectHandler'); + $property->setAccessible(true); + $property->setValue(null); + + // clear suite object handler value to inject parsed content + $property = new \ReflectionProperty(SuiteObjectHandler::class, 'SUITE_OBJECT_HANLDER_INSTANCE'); + $property->setAccessible(true); + $property->setValue(null); + + $mockDataParser = AspectMock::double(TestDataParser::class, ['readTestData' => $testData])->make(); + $mockSuiteDataParser = AspectMock::double(SuiteDataParser::class, ['readSuiteData' => $suiteData])->make(); + $mockGroupClass = AspectMock::double( + GroupClassGenerator::class, + ['generateGroupClass' => 'namespace'] + )->make(); + $mockSuiteClass = AspectMock::double(SuiteGenerator::class, ['generateRelevantGroupTests' => null])->make(); + $instance = AspectMock::double( + ObjectManager::class, + ['create' => function ($clazz) use ( + $mockDataParser, + $mockSuiteDataParser, + $mockGroupClass, + $mockSuiteClass + ) { + if ($clazz == TestDataParser::class) { + return $mockDataParser; + } + if ($clazz == SuiteDataParser::class) { + return $mockSuiteDataParser; + } + if ($clazz == GroupClassGenerator::class) { + return $mockGroupClass; + } + if ($clazz == SuiteGenerator::class) { + return $mockSuiteClass; + } + }] + )->make(); + // bypass the private constructor + AspectMock::double(ObjectManagerFactory::class, ['getObjectManager' => $instance]); + + $property = new \ReflectionProperty(SuiteGenerator::class, 'groupClassGenerator'); + $property->setAccessible(true); + $property->setValue($instance, $instance); + + } +} diff --git a/src/Magento/FunctionalTestingFramework/Suite/SuiteGenerator.php b/src/Magento/FunctionalTestingFramework/Suite/SuiteGenerator.php index 9c96742b4..f2af5cd31 100644 --- a/src/Magento/FunctionalTestingFramework/Suite/SuiteGenerator.php +++ b/src/Magento/FunctionalTestingFramework/Suite/SuiteGenerator.php @@ -74,10 +74,7 @@ public static function getInstance() */ public function generateAllSuites($testManifest) { - $suites = array_keys(SuiteObjectHandler::getInstance()->getAllObjects()); - if ($testManifest != null) { - $suites = $testManifest->getSuiteConfig(); - } + $suites = $testManifest->getSuiteConfig(); foreach ($suites as $suiteName => $suiteContent) { $firstElement = array_values($suiteContent)[0]; @@ -94,37 +91,6 @@ public function generateAllSuites($testManifest) } } - /** - * Returns an array of tests contained within suites as keys pointed at the name of their corresponding suite. - * - * @return array - */ - public function getTestsReferencedInSuites() - { - $testsReferencedInSuites = []; - $suites = SuiteObjectHandler::getInstance()->getAllObjects(); - - // see if we have a specific suite configuration. - if (!empty($this->suiteReferences)) { - $suites = array_intersect_key($suites, $this->suiteReferences); - } - - foreach ($suites as $suite) { - /** @var SuiteObject $suite */ - $test_keys = array_keys($suite->getTests()); - - // see if we need to filter which tests we'll be generating. - if (array_key_exists($suite->getName(), $this->suiteReferences)) { - $test_keys = $this->suiteReferences[$suite->getName()] ?? $test_keys; - } - - $testToSuiteName = array_fill_keys($test_keys, [$suite->getName()]); - $testsReferencedInSuites = array_merge_recursive($testsReferencedInSuites, $testToSuiteName); - } - - return $testsReferencedInSuites; - } - /** * Function which takes a suite name and generates corresponding dir, test files, group class, and updates * yml configuration for group run. From ff0fbc2e6bdf1190d491cca3d5f2cb25cfd5d1a0 Mon Sep 17 00:00:00 2001 From: KevinBKozan Date: Tue, 1 May 2018 15:37:48 -0500 Subject: [PATCH 022/111] MQE-812: Tests/Action Groups should infer order based on the top level argument - static check fix. --- src/Magento/FunctionalTestingFramework/Test/Config/Dom.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Magento/FunctionalTestingFramework/Test/Config/Dom.php b/src/Magento/FunctionalTestingFramework/Test/Config/Dom.php index 3d8c2c4ba..4c2c9e4e0 100644 --- a/src/Magento/FunctionalTestingFramework/Test/Config/Dom.php +++ b/src/Magento/FunctionalTestingFramework/Test/Config/Dom.php @@ -122,7 +122,7 @@ protected function appendMergePointerToActions($testNode, $pointerType, $pointer } if ($currentNode->hasAttribute('stepKey')) { if ($currentNode->hasAttribute($pointerType) && $testNode->hasAttribute($pointerType)) { - $errorMsg = "Actions cannot have a before/after pointer if they are in a test that provides a merge pointer."; + $errorMsg = "Actions cannot have merge pointers if contained in tests that has a merge pointer."; $errorMsg .= "\n\tstepKey: {$currentNode->getAttribute('stepKey')}\tin file: {$filename}"; $exceptionCollector->addError($filename, $errorMsg); continue; From c030dccb57d5b0ed91280635fcccce85e16965cb Mon Sep 17 00:00:00 2001 From: KevinBKozan Date: Wed, 2 May 2018 11:16:42 -0500 Subject: [PATCH 023/111] MQE-812: Tests/Action Groups should infer order based on the top level argument - updated ActionGroups to work with this - need verification tests still --- .../Test/Config/ActionGroupDom.php | 18 +++++++++++++++++- .../Test/Config/Dom.php | 4 ++-- .../Test/Util/ActionGroupObjectExtractor.php | 6 +++++- .../Test/Util/TestObjectExtractor.php | 7 ++++++- .../Test/etc/actionGroupSchema.xsd | 2 ++ .../Test/etc/mergedTestSchema.xsd | 4 ++-- 6 files changed, 34 insertions(+), 7 deletions(-) diff --git a/src/Magento/FunctionalTestingFramework/Test/Config/ActionGroupDom.php b/src/Magento/FunctionalTestingFramework/Test/Config/ActionGroupDom.php index 794007dde..bfe1c243f 100644 --- a/src/Magento/FunctionalTestingFramework/Test/Config/ActionGroupDom.php +++ b/src/Magento/FunctionalTestingFramework/Test/Config/ActionGroupDom.php @@ -30,9 +30,25 @@ public function initDom($xml, $filename = null, $exceptionCollector = null) /** @var \DOMElement $actionGroupNode */ $actionGroupNode->setAttribute(self::TEST_META_FILENAME_ATTRIBUTE, $filename); $this->validateDomStepKeys($actionGroupNode, $filename, 'Action Group', $exceptionCollector); + if ($actionGroupNode->getAttribute(self::TEST_MERGE_POINTER_AFTER) !== "") { + $this->appendMergePointerToActions( + $actionGroupNode, + self::TEST_MERGE_POINTER_AFTER, + $actionGroupNode->getAttribute(self::TEST_MERGE_POINTER_AFTER), + $filename, + $exceptionCollector + ); + } elseif ($actionGroupNode->getAttribute(self::TEST_MERGE_POINTER_BEFORE) !== "") { + $this->appendMergePointerToActions( + $actionGroupNode, + self::TEST_MERGE_POINTER_BEFORE, + $actionGroupNode->getAttribute(self::TEST_MERGE_POINTER_BEFORE), + $filename, + $exceptionCollector + ); + } } } - return $dom; } } diff --git a/src/Magento/FunctionalTestingFramework/Test/Config/Dom.php b/src/Magento/FunctionalTestingFramework/Test/Config/Dom.php index 4c2c9e4e0..ffb1e79be 100644 --- a/src/Magento/FunctionalTestingFramework/Test/Config/Dom.php +++ b/src/Magento/FunctionalTestingFramework/Test/Config/Dom.php @@ -17,8 +17,8 @@ class Dom extends \Magento\FunctionalTestingFramework\Config\Dom const TEST_META_FILENAME_ATTRIBUTE = 'filename'; const TEST_META_NAME_ATTRIBUTE = 'name'; const TEST_HOOK_NAMES = ["after", "before"]; - const TEST_MERGE_POINTER_BEFORE = "before"; - const TEST_MERGE_POINTER_AFTER = "after"; + const TEST_MERGE_POINTER_BEFORE = "insertBefore"; + const TEST_MERGE_POINTER_AFTER = "insertAfter"; /** * TestDom constructor. diff --git a/src/Magento/FunctionalTestingFramework/Test/Util/ActionGroupObjectExtractor.php b/src/Magento/FunctionalTestingFramework/Test/Util/ActionGroupObjectExtractor.php index aeef21760..783fb1c5a 100644 --- a/src/Magento/FunctionalTestingFramework/Test/Util/ActionGroupObjectExtractor.php +++ b/src/Magento/FunctionalTestingFramework/Test/Util/ActionGroupObjectExtractor.php @@ -18,6 +18,8 @@ class ActionGroupObjectExtractor extends BaseObjectExtractor const DEFAULT_VALUE = 'defaultValue'; const ACTION_GROUP_ARGUMENTS = 'arguments'; const FILENAME = 'filename'; + const ACTION_GROUP_INSERT_BEFORE = "insertBefore"; + const ACTION_GROUP_INSERT_AFTER = "insertAfter"; /** * Action Object Extractor for converting actions into objects @@ -49,7 +51,9 @@ public function extractActionGroup($actionGroupData) self::NODE_NAME, self::ACTION_GROUP_ARGUMENTS, self::NAME, - self::FILENAME + self::FILENAME, + self::ACTION_GROUP_INSERT_BEFORE, + self::ACTION_GROUP_INSERT_AFTER ); // TODO filename is now available to the ActionGroupObject, integrate this into debug and error statements diff --git a/src/Magento/FunctionalTestingFramework/Test/Util/TestObjectExtractor.php b/src/Magento/FunctionalTestingFramework/Test/Util/TestObjectExtractor.php index cfe603a5f..d96170c0d 100644 --- a/src/Magento/FunctionalTestingFramework/Test/Util/TestObjectExtractor.php +++ b/src/Magento/FunctionalTestingFramework/Test/Util/TestObjectExtractor.php @@ -23,6 +23,9 @@ class TestObjectExtractor extends BaseObjectExtractor const TEST_FAILED_HOOK = 'failed'; const TEST_BEFORE_ATTRIBUTE = 'before'; const TEST_AFTER_ATTRIBUTE = 'after'; + const TEST_INSERT_BEFORE = 'insertBefore'; + const TEST_INSERT_AFTER = 'insertAfter'; + const TEST_FILENAME = 'filename'; /** * Action Object Extractor object @@ -79,7 +82,9 @@ public function extractTestData($testData) self::TEST_BEFORE_HOOK, self::TEST_AFTER_HOOK, self::TEST_FAILED_HOOK, - 'filename' + self::TEST_INSERT_BEFORE, + self::TEST_INSERT_AFTER, + self::TEST_FILENAME ); if (array_key_exists(self::TEST_ANNOTATIONS, $testData)) { diff --git a/src/Magento/FunctionalTestingFramework/Test/etc/actionGroupSchema.xsd b/src/Magento/FunctionalTestingFramework/Test/etc/actionGroupSchema.xsd index 74badd40c..5df793643 100644 --- a/src/Magento/FunctionalTestingFramework/Test/etc/actionGroupSchema.xsd +++ b/src/Magento/FunctionalTestingFramework/Test/etc/actionGroupSchema.xsd @@ -33,6 +33,8 @@ + + diff --git a/src/Magento/FunctionalTestingFramework/Test/etc/mergedTestSchema.xsd b/src/Magento/FunctionalTestingFramework/Test/etc/mergedTestSchema.xsd index 125bd8baf..ecd058581 100644 --- a/src/Magento/FunctionalTestingFramework/Test/etc/mergedTestSchema.xsd +++ b/src/Magento/FunctionalTestingFramework/Test/etc/mergedTestSchema.xsd @@ -70,8 +70,8 @@ - - + + From b350296881509879682a3147b56774f4ae94be5e Mon Sep 17 00:00:00 2001 From: KevinBKozan Date: Wed, 2 May 2018 14:34:08 -0500 Subject: [PATCH 024/111] MQE-812: Tests/Action Groups should infer order based on the top level argument - verification updates - dom.php refactor and fixes. --- .../Resources/MergeMassViaInsertAfter.txt | 37 +++++++++++++++++++ .../Resources/MergeMassViaInsertBefore.txt | 37 +++++++++++++++++++ .../ActionGroup/FunctionalActionGroup.xml | 10 +++++ .../MergeFunctionalActionGroup.xml | 12 ++++++ .../Test/ActionGroupFunctionalTest.xml | 6 +++ .../TestModule/Test/BasicFunctionalTest.xml | 10 +++++ .../TestModule/Test/MergeFunctionalTest.xml | 10 +++++ .../Tests/ActionGroupMergeGenerationTest.php | 22 +++++++++++ .../Tests/MergedGenerationTest.php | 22 +++++++++++ .../Test/Config/Dom.php | 20 +++++++--- 10 files changed, 180 insertions(+), 6 deletions(-) create mode 100644 dev/tests/verification/Resources/MergeMassViaInsertAfter.txt create mode 100644 dev/tests/verification/Resources/MergeMassViaInsertBefore.txt diff --git a/dev/tests/verification/Resources/MergeMassViaInsertAfter.txt b/dev/tests/verification/Resources/MergeMassViaInsertAfter.txt new file mode 100644 index 000000000..f44f58e42 --- /dev/null +++ b/dev/tests/verification/Resources/MergeMassViaInsertAfter.txt @@ -0,0 +1,37 @@ +fillField("#foo", "foo"); + $I->fillField("#bar", "bar"); + $I->click("#mergeOne"); + $I->click("#mergeTwo"); + $I->click("#mergeThree"); + $I->fillField("#baz", "baz"); + } +} diff --git a/dev/tests/verification/Resources/MergeMassViaInsertBefore.txt b/dev/tests/verification/Resources/MergeMassViaInsertBefore.txt new file mode 100644 index 000000000..2385eda39 --- /dev/null +++ b/dev/tests/verification/Resources/MergeMassViaInsertBefore.txt @@ -0,0 +1,37 @@ +fillField("#foo", "foo"); + $I->click("#mergeOne"); + $I->click("#mergeTwo"); + $I->click("#mergeThree"); + $I->fillField("#bar", "bar"); + $I->fillField("#baz", "baz"); + } +} diff --git a/dev/tests/verification/TestModule/ActionGroup/FunctionalActionGroup.xml b/dev/tests/verification/TestModule/ActionGroup/FunctionalActionGroup.xml index 843c66739..1afc847a6 100644 --- a/dev/tests/verification/TestModule/ActionGroup/FunctionalActionGroup.xml +++ b/dev/tests/verification/TestModule/ActionGroup/FunctionalActionGroup.xml @@ -50,4 +50,14 @@ + + + + + + + + + + diff --git a/dev/tests/verification/TestModule/ActionGroup/MergeFunctionalActionGroup.xml b/dev/tests/verification/TestModule/ActionGroup/MergeFunctionalActionGroup.xml index bd478e645..e993395c6 100644 --- a/dev/tests/verification/TestModule/ActionGroup/MergeFunctionalActionGroup.xml +++ b/dev/tests/verification/TestModule/ActionGroup/MergeFunctionalActionGroup.xml @@ -13,4 +13,16 @@ + + + + + + + + + + + + diff --git a/dev/tests/verification/TestModule/Test/ActionGroupFunctionalTest.xml b/dev/tests/verification/TestModule/Test/ActionGroupFunctionalTest.xml index ad372b838..03a71f056 100644 --- a/dev/tests/verification/TestModule/Test/ActionGroupFunctionalTest.xml +++ b/dev/tests/verification/TestModule/Test/ActionGroupFunctionalTest.xml @@ -182,4 +182,10 @@ + + + + + + diff --git a/dev/tests/verification/TestModule/Test/BasicFunctionalTest.xml b/dev/tests/verification/TestModule/Test/BasicFunctionalTest.xml index bd7c23522..92b04e0ad 100644 --- a/dev/tests/verification/TestModule/Test/BasicFunctionalTest.xml +++ b/dev/tests/verification/TestModule/Test/BasicFunctionalTest.xml @@ -119,4 +119,14 @@ + + + + + + + + + + \ No newline at end of file diff --git a/dev/tests/verification/TestModule/Test/MergeFunctionalTest.xml b/dev/tests/verification/TestModule/Test/MergeFunctionalTest.xml index 75c62b36f..553f3d251 100644 --- a/dev/tests/verification/TestModule/Test/MergeFunctionalTest.xml +++ b/dev/tests/verification/TestModule/Test/MergeFunctionalTest.xml @@ -45,4 +45,14 @@ + + + + + + + + + + diff --git a/dev/tests/verification/Tests/ActionGroupMergeGenerationTest.php b/dev/tests/verification/Tests/ActionGroupMergeGenerationTest.php index 9176f3955..0361bec75 100644 --- a/dev/tests/verification/Tests/ActionGroupMergeGenerationTest.php +++ b/dev/tests/verification/Tests/ActionGroupMergeGenerationTest.php @@ -108,4 +108,26 @@ public function testArgumentWithSameNameAsElement() { $this->generateAndCompareTest('ArgumentWithSameNameAsElement'); } + + /** + * Test an action group with a merge counterpart that's merged via insertBefore + * + * @throws \Exception + * @throws \Magento\FunctionalTestingFramework\Exceptions\TestReferenceException + */ + public function testMergedActionGroupViaInsertBefore() + { + $this->generateAndCompareTest('ActionGroupMergedViaInsertBefore'); + } + + /** + * Test an action group with a merge counterpart that's merged via insertAfter + * + * @throws \Exception + * @throws \Magento\FunctionalTestingFramework\Exceptions\TestReferenceException + */ + public function testMergedActionGroupViaInsertAfter() + { + $this->generateAndCompareTest('ActionGroupMergedViaInsertAfter'); + } } diff --git a/dev/tests/verification/Tests/MergedGenerationTest.php b/dev/tests/verification/Tests/MergedGenerationTest.php index fe2990b76..9ef22fe09 100644 --- a/dev/tests/verification/Tests/MergedGenerationTest.php +++ b/dev/tests/verification/Tests/MergedGenerationTest.php @@ -40,4 +40,26 @@ public function testParsedArray() $entity = DataObjectHandler::getInstance()->getObject('testEntity'); $this->assertCount(3, $entity->getLinkedEntities()); } + + /** + * Tests generation of a test merge file via insertBefore + * + * @throws \Exception + * @throws \Magento\FunctionalTestingFramework\Exceptions\TestReferenceException + */ + public function testMergeMassViaInsertBefore() + { + $this->generateAndCompareTest('MergeMassViaInsertBefore'); + } + + /** + * Tests generation of a test merge file via insertBefore + * + * @throws \Exception + * @throws \Magento\FunctionalTestingFramework\Exceptions\TestReferenceException + */ + public function testMergeMassViaInsertAfter() + { + $this->generateAndCompareTest('MergeMassViaInsertAfter'); + } } diff --git a/src/Magento/FunctionalTestingFramework/Test/Config/Dom.php b/src/Magento/FunctionalTestingFramework/Test/Config/Dom.php index ffb1e79be..ea516dd22 100644 --- a/src/Magento/FunctionalTestingFramework/Test/Config/Dom.php +++ b/src/Magento/FunctionalTestingFramework/Test/Config/Dom.php @@ -10,6 +10,7 @@ use Magento\FunctionalTestingFramework\Exceptions\XmlException; use Magento\FunctionalTestingFramework\Config\Dom\NodeMergingConfig; use Magento\FunctionalTestingFramework\Config\Dom\NodePathMatcher; +use Magento\FunctionalTestingFramework\Test\Objects\ActionObject; class Dom extends \Magento\FunctionalTestingFramework\Config\Dom { @@ -106,28 +107,35 @@ public function merge($xml, $filename = null, $exceptionCollector = null) * Parses DOM Structure's actions and appends a before/after attribute along with the parent's stepkey reference. * * @param \DOMElement $testNode - * @param string $pointerType - * @param string $pointerKey + * @param string $insertType + * @param string $insertKey * @param string $filename * @param ExceptionCollector $exceptionCollector * @return void */ - protected function appendMergePointerToActions($testNode, $pointerType, $pointerKey, $filename, $exceptionCollector) + protected function appendMergePointerToActions($testNode, $insertType, $insertKey, $filename, $exceptionCollector) { $childNodes = $testNode->childNodes; + $previousStepKey = $insertKey; + $actionInsertType = ActionObject::MERGE_ACTION_ORDER_AFTER; + if ($insertType == self::TEST_MERGE_POINTER_BEFORE) { + $actionInsertType = ActionObject::MERGE_ACTION_ORDER_BEFORE; + } for ($i = 0; $i < $childNodes->length; $i++) { $currentNode = $childNodes->item($i); if (!is_a($currentNode, \DOMElement::class)) { continue; } if ($currentNode->hasAttribute('stepKey')) { - if ($currentNode->hasAttribute($pointerType) && $testNode->hasAttribute($pointerType)) { + if ($currentNode->hasAttribute($insertType) && $testNode->hasAttribute($insertType)) { $errorMsg = "Actions cannot have merge pointers if contained in tests that has a merge pointer."; $errorMsg .= "\n\tstepKey: {$currentNode->getAttribute('stepKey')}\tin file: {$filename}"; $exceptionCollector->addError($filename, $errorMsg); - continue; } - $currentNode->setAttribute($pointerType, $pointerKey); + $currentNode->setAttribute($actionInsertType, $previousStepKey); + $previousStepKey = $currentNode->getAttribute('stepKey'); + // All actions after the first need to insert AFTER. + $actionInsertType = ActionObject::MERGE_ACTION_ORDER_AFTER; } } } From a4249b50697a25b7be17239a210f586a5367051b Mon Sep 17 00:00:00 2001 From: KevinBKozan Date: Wed, 2 May 2018 15:23:55 -0500 Subject: [PATCH 025/111] MQE-812: Tests/Action Groups should infer order based on the top level argument - Fix to old verification tests, found and fixed a bug with old merging. - final test fixes. --- .../ActionGroupMergedViaInsertAfter.txt | 37 +++++++++++++++++++ .../ActionGroupMergedViaInsertBefore.txt | 37 +++++++++++++++++++ .../Resources/MergedActionGroupTest.txt | 4 +- .../Test/Objects/ActionGroupObject.php | 8 +++- 4 files changed, 83 insertions(+), 3 deletions(-) create mode 100644 dev/tests/verification/Resources/ActionGroupMergedViaInsertAfter.txt create mode 100644 dev/tests/verification/Resources/ActionGroupMergedViaInsertBefore.txt diff --git a/dev/tests/verification/Resources/ActionGroupMergedViaInsertAfter.txt b/dev/tests/verification/Resources/ActionGroupMergedViaInsertAfter.txt new file mode 100644 index 000000000..63565d7f8 --- /dev/null +++ b/dev/tests/verification/Resources/ActionGroupMergedViaInsertAfter.txt @@ -0,0 +1,37 @@ +fillField("#foo", "foo"); + $I->fillField("#bar", "bar"); + $I->click("#mergeOne"); + $I->click("#mergeTwo"); + $I->click("#mergeThree"); + $I->fillField("#baz", "baz"); + } +} diff --git a/dev/tests/verification/Resources/ActionGroupMergedViaInsertBefore.txt b/dev/tests/verification/Resources/ActionGroupMergedViaInsertBefore.txt new file mode 100644 index 000000000..f44e15b4e --- /dev/null +++ b/dev/tests/verification/Resources/ActionGroupMergedViaInsertBefore.txt @@ -0,0 +1,37 @@ +fillField("#foo", "foo"); + $I->click("#mergeOne"); + $I->click("#mergeTwo"); + $I->click("#mergeThree"); + $I->fillField("#bar", "bar"); + $I->fillField("#baz", "baz"); + } +} diff --git a/dev/tests/verification/Resources/MergedActionGroupTest.txt b/dev/tests/verification/Resources/MergedActionGroupTest.txt index ab9b46f8d..cbaa87f7e 100644 --- a/dev/tests/verification/Resources/MergedActionGroupTest.txt +++ b/dev/tests/verification/Resources/MergedActionGroupTest.txt @@ -70,9 +70,9 @@ class MergedActionGroupTestCest */ public function MergedActionGroupTest(AcceptanceTester $I) { - $I->see("#element .Jane"); $I->see(".merge .Jane"); - $I->click(".merge .Dane"); + $I->see("#element .Jane"); $I->amOnPage("/Jane/Dane.html"); + $I->click(".merge .Dane"); } } diff --git a/src/Magento/FunctionalTestingFramework/Test/Objects/ActionGroupObject.php b/src/Magento/FunctionalTestingFramework/Test/Objects/ActionGroupObject.php index fdef1107e..f1db6451a 100644 --- a/src/Magento/FunctionalTestingFramework/Test/Objects/ActionGroupObject.php +++ b/src/Magento/FunctionalTestingFramework/Test/Objects/ActionGroupObject.php @@ -136,6 +136,12 @@ private function getResolvedActionsWithArgs($arguments, $actionReferenceKey) ); } + // translate 0/1 back to before/after + $orderOffset = ActionObject::MERGE_ACTION_ORDER_BEFORE; + if ($action->getOrderOffset() === 1) { + $orderOffset = ActionObject::MERGE_ACTION_ORDER_AFTER; + } + // we append the action reference key to any linked action and the action's merge key as the user might // use this action group multiple times in the same test. $resolvedActions[$action->getStepKey() . ucfirst($actionReferenceKey)] = new ActionObject( @@ -143,7 +149,7 @@ private function getResolvedActionsWithArgs($arguments, $actionReferenceKey) $action->getType(), array_replace_recursive($action->getCustomActionAttributes(), $newActionAttributes), $action->getLinkedAction() == null ? null : $action->getLinkedAction() . ucfirst($actionReferenceKey), - $action->getOrderOffset(), + $orderOffset, [self::ACTION_GROUP_ORIGIN_NAME => $this->name, self::ACTION_GROUP_ORIGIN_TEST_REF => $actionReferenceKey] ); From 371680df553434ac053885b980adb7267dd1288f Mon Sep 17 00:00:00 2001 From: Alexander Calandra Date: Mon, 30 Apr 2018 13:13:21 -0500 Subject: [PATCH 026/111] MQE-985: Empty Suite configuration should not generate - Adding check for empty suites --- .../Suite/SuiteGeneratorTest.php | 25 +++++++++++++++++++ .../Suite/Util/SuiteObjectExtractor.php | 24 ++++++++++++++---- 2 files changed, 44 insertions(+), 5 deletions(-) diff --git a/dev/tests/unit/Magento/FunctionalTestFramework/Suite/SuiteGeneratorTest.php b/dev/tests/unit/Magento/FunctionalTestFramework/Suite/SuiteGeneratorTest.php index e7c607729..8eb486144 100644 --- a/dev/tests/unit/Magento/FunctionalTestFramework/Suite/SuiteGeneratorTest.php +++ b/dev/tests/unit/Magento/FunctionalTestFramework/Suite/SuiteGeneratorTest.php @@ -13,6 +13,7 @@ use Magento\FunctionalTestingFramework\Suite\Handlers\SuiteObjectHandler; use Magento\FunctionalTestingFramework\Suite\Parsers\SuiteDataParser; use Magento\FunctionalTestingFramework\Test\Handlers\TestObjectHandler; +use Magento\FunctionalTestingFramework\Test\Util\TestObjectExtractor; use Magento\FunctionalTestingFramework\Test\Parsers\TestDataParser; use Magento\FunctionalTestingFramework\Util\Manifest\DefaultTestManifest; use PHPUnit\Framework\TestCase; @@ -87,6 +88,30 @@ public function testGenerateAllSuites() $this->expectOutputString("Suite basicTestSuite generated to _generated/basicTestSuite." . PHP_EOL); } + /** + * Tests attempting to generate a suite with no included/excluded tests and no hooks + * @throws \Exception + */ + public function testGenerateEmptySuite() + { + $suiteDataArrayBuilder = new SuiteDataArrayBuilder(); + $mockData = $suiteDataArrayBuilder + ->withName('basicTestSuite') + ->build(); + unset($mockData['suites']['basicTestSuite'][TestObjectExtractor::TEST_BEFORE_HOOK]); + unset($mockData['suites']['basicTestSuite'][TestObjectExtractor::TEST_AFTER_HOOK]); + + $mockTestData = null; + $this->setMockTestAndSuiteParserOutput($mockTestData, $mockData); + + // set expected error message + $this->expectExceptionMessage("Suites must not be empty. Suite: \"basicTestSuite\""); + + // parse and generate suite object with mocked data + $mockSuiteGenerator = SuiteGenerator::getInstance(); + $mockSuiteGenerator->generateSuite("basicTestSuite"); + } + /** * Function used to set mock for parser return and force init method to run between tests. * diff --git a/src/Magento/FunctionalTestingFramework/Suite/Util/SuiteObjectExtractor.php b/src/Magento/FunctionalTestingFramework/Suite/Util/SuiteObjectExtractor.php index 2e600806b..4f72042bf 100644 --- a/src/Magento/FunctionalTestingFramework/Suite/Util/SuiteObjectExtractor.php +++ b/src/Magento/FunctionalTestingFramework/Suite/Util/SuiteObjectExtractor.php @@ -89,11 +89,6 @@ public function parseSuiteDataIntoObjects($parsedSuiteData) $includeTests = $this->extractTestObjectsFromSuiteRef($groupTestsToInclude); $excludeTests = $this->extractTestObjectsFromSuiteRef($groupTestsToExclude); - // add all test if include tests is completely empty - if (empty($includeTests)) { - $includeTests = TestObjectHandler::getInstance()->getAllObjects(); - } - // parse any object hooks if (array_key_exists(TestObjectExtractor::TEST_BEFORE_HOOK, $parsedSuite)) { $suiteHooks[TestObjectExtractor::TEST_BEFORE_HOOK] = $testHookObjectExtractor->extractHook( @@ -109,12 +104,31 @@ public function parseSuiteDataIntoObjects($parsedSuiteData) $parsedSuite[TestObjectExtractor::TEST_AFTER_HOOK] ); } + if (count($suiteHooks) == 1) { throw new XmlException(sprintf( "Suites that contain hooks must contain both a 'before' and an 'after' hook. Suite: \"%s\"", $parsedSuite[self::NAME] )); } + // check if suite hooks are empty/not included and there are no included tests/groups/modules + $noHooks = count($suiteHooks) == 0 || + ( + empty($suiteHooks['before']->getActions()) && + empty($suiteHooks['after']->getActions()) + ); + // if suite body is empty throw error + if ($noHooks && empty($includeTests) && empty($excludeTests)) { + throw new XmlException(sprintf( + "Suites must not be empty. Suite: \"%s\"", + $parsedSuite[self::NAME] + )); + } + + // add all test if include tests is completely empty + if (empty($includeTests)) { + $includeTests = TestObjectHandler::getInstance()->getAllObjects(); + } // create the new suite object $suiteObjects[$parsedSuite[self::NAME]] = new SuiteObject( From 40b85ab8c193cfc5f15bd3ee7e711a02858ebe9e Mon Sep 17 00:00:00 2001 From: Kevin Kozan Date: Fri, 4 May 2018 21:17:42 +0300 Subject: [PATCH 027/111] =?UTF-8?q?MQE-922:=20Periods=20in=20Allure=20repo?= =?UTF-8?q?rt=20execution=20test=20body=20appear=20as=20=E2=80=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - MagentoAllureAdapter now overrides the stepBefore event, fixes replacing of `.` --- .../Allure/Adapter/MagentoAllureAdapter.php | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/src/Magento/FunctionalTestingFramework/Allure/Adapter/MagentoAllureAdapter.php b/src/Magento/FunctionalTestingFramework/Allure/Adapter/MagentoAllureAdapter.php index 393f29008..3d12cb6e1 100644 --- a/src/Magento/FunctionalTestingFramework/Allure/Adapter/MagentoAllureAdapter.php +++ b/src/Magento/FunctionalTestingFramework/Allure/Adapter/MagentoAllureAdapter.php @@ -8,7 +8,9 @@ use Magento\FunctionalTestingFramework\Data\Argument\Interpreter\NullType; use Magento\FunctionalTestingFramework\Suite\Handlers\SuiteObjectHandler; use Yandex\Allure\Adapter\AllureAdapter; +use Yandex\Allure\Adapter\Event\StepStartedEvent; use Codeception\Event\SuiteEvent; +use Codeception\Event\StepEvent; /** * Class MagentoAllureAdapter @@ -91,4 +93,28 @@ private function sanitizeGroupName($group) $originalName = in_array($originalName, $suiteNames) ? $originalName : $group; return $originalName; } + + /** + * Override of parent method, only different to prevent replacing of . to • + * + * @param StepEvent $stepEvent + * @return void + */ + public function stepBefore(StepEvent $stepEvent) + { + //Hard set to 200; we don't expose this config in MFTF + $argumentsLength = 200; + $stepAction = $stepEvent->getStep()->getHumanizedActionWithoutArguments(); + $stepArgs = $stepEvent->getStep()->getArgumentsAsString($argumentsLength); + + if (!trim($stepAction)) { + $stepAction = $stepEvent->getStep()->getMetaStep()->getHumanizedActionWithoutArguments(); + $stepArgs = $stepEvent->getStep()->getMetaStep()->getArgumentsAsString($argumentsLength); + } + + $stepName = $stepAction . ' ' . $stepArgs; + + $this->emptyStep = false; + $this->getLifecycle()->fire(new StepStartedEvent($stepName)); + } } From 131f5c6dbe6d7902ee4875ec3de285f43593e6ab Mon Sep 17 00:00:00 2001 From: aljcalandra Date: Fri, 4 May 2018 14:47:02 -0500 Subject: [PATCH 028/111] MQE-983: Unit Test for Module Resolver - Made Module Blacklist Test more Explicit --- .../Util/ModuleResolverTest.php | 23 ++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/dev/tests/unit/Magento/FunctionalTestFramework/Util/ModuleResolverTest.php b/dev/tests/unit/Magento/FunctionalTestFramework/Util/ModuleResolverTest.php index f80ce0223..0e80b2f1d 100644 --- a/dev/tests/unit/Magento/FunctionalTestFramework/Util/ModuleResolverTest.php +++ b/dev/tests/unit/Magento/FunctionalTestFramework/Util/ModuleResolverTest.php @@ -16,6 +16,8 @@ class ModuleResolverTest extends TestCase { + private $index = 0; + /** * remove all registered test doubles */ @@ -103,11 +105,26 @@ public function testGetCustomModulePath() */ public function testGetModulePathsBlacklist() { - $this->setMockResolverClass(false, ["Magento_TestModule"], null, null, ['somePath']); - $this->expectOutputString("Excluding module: 0" . PHP_EOL); + $this->setMockResolverClass( + false, + null, + null, + null, + function ($arg1, $arg2) { + if ($arg2 === "") { + $mockValue = ["somePath" => "somePath"]; + } elseif (strpos($arg1, "app")) { + $mockValue = ["otherPath" => "otherPath"]; + } else { + $mockValue = ["lastPath" => "lastPath"]; + } + return $mockValue; + } + ); + $this->expectOutputString("Excluding module: somePath" . PHP_EOL); $resolver = ModuleResolver::getInstance(); $this->setMockResolverProperties($resolver, null, null, ["somePath"]); - $this->assertEquals(['somePath', 'somePath'], $resolver->getModulesPath()); + $this->assertEquals(["otherPath", "lastPath"], $resolver->getModulesPath()); } /** From 2b92b62ea22c3354a9a4d44e61c78382b0023e45 Mon Sep 17 00:00:00 2001 From: aljcalandra Date: Fri, 4 May 2018 15:51:23 -0500 Subject: [PATCH 029/111] MQE-983: Unit Test for Module Resolver - Removed unused property --- .../Magento/FunctionalTestFramework/Util/ModuleResolverTest.php | 2 -- 1 file changed, 2 deletions(-) diff --git a/dev/tests/unit/Magento/FunctionalTestFramework/Util/ModuleResolverTest.php b/dev/tests/unit/Magento/FunctionalTestFramework/Util/ModuleResolverTest.php index 0e80b2f1d..ea670c4a6 100644 --- a/dev/tests/unit/Magento/FunctionalTestFramework/Util/ModuleResolverTest.php +++ b/dev/tests/unit/Magento/FunctionalTestFramework/Util/ModuleResolverTest.php @@ -16,8 +16,6 @@ class ModuleResolverTest extends TestCase { - private $index = 0; - /** * remove all registered test doubles */ From 091b10e0775e3f9de08dbfe7f578d5d7df203393 Mon Sep 17 00:00:00 2001 From: KevinBKozan Date: Mon, 7 May 2018 11:19:57 -0500 Subject: [PATCH 030/111] MQE-980: [Annotations] tag defaults to module - CR fixes, test updates --- .../Test/Handlers/TestObjectHandlerTest.php | 38 +++++++++++++++++++ .../Test/Util/TestObjectExtractor.php | 6 ++- 2 files changed, 42 insertions(+), 2 deletions(-) diff --git a/dev/tests/unit/Magento/FunctionalTestFramework/Test/Handlers/TestObjectHandlerTest.php b/dev/tests/unit/Magento/FunctionalTestFramework/Test/Handlers/TestObjectHandlerTest.php index 6ef9af12a..860c8d3bb 100644 --- a/dev/tests/unit/Magento/FunctionalTestFramework/Test/Handlers/TestObjectHandlerTest.php +++ b/dev/tests/unit/Magento/FunctionalTestFramework/Test/Handlers/TestObjectHandlerTest.php @@ -141,6 +141,44 @@ public function testGetTestsByGroup() $this->assertArrayNotHasKey('excludeTest', $tests); } + /** + * Tests the function used to parse and determine a test's Module (used in allure Features annotation) + * + * @throws \Exception + */ + public function testGetTestWithModuleName() + { + // set up Test Data + $moduleExpected = "SomeTestModule"; + $filepath = DIRECTORY_SEPARATOR . + "user" . + "magento2ce" . DIRECTORY_SEPARATOR . + "dev" . DIRECTORY_SEPARATOR . + "tests" . DIRECTORY_SEPARATOR . + "acceptance" . DIRECTORY_SEPARATOR . + "tests" . DIRECTORY_SEPARATOR . + $moduleExpected . DIRECTORY_SEPARATOR . + "Tests" . DIRECTORY_SEPARATOR . + "text.xml"; + // set up mock data + $testDataArrayBuilder = new TestDataArrayBuilder(); + $mockData = $testDataArrayBuilder + ->withAnnotations() + ->withFailedHook() + ->withAfterHook() + ->withBeforeHook() + ->withTestActions() + ->withFileName($filepath) + ->build(); + $this->setMockParserOutput(['tests' => $mockData]); + // Execute Test Method + $toh = TestObjectHandler::getInstance(); + $actualTestObject = $toh->getObject($testDataArrayBuilder->testName); + $moduleName = $actualTestObject->getAnnotations()["features"][0]; + //performAsserts + $this->assertEquals($moduleExpected, $moduleName); + } + /** * Function used to set mock for parser return and force init method to run between tests. * diff --git a/src/Magento/FunctionalTestingFramework/Test/Util/TestObjectExtractor.php b/src/Magento/FunctionalTestingFramework/Test/Util/TestObjectExtractor.php index 27d977ba0..312885ca6 100644 --- a/src/Magento/FunctionalTestingFramework/Test/Util/TestObjectExtractor.php +++ b/src/Magento/FunctionalTestingFramework/Test/Util/TestObjectExtractor.php @@ -134,11 +134,13 @@ public function extractTestData($testData) */ private function extractModuleName($path) { - if ($path === "") { + if (empty($path)) { return "NO MODULE DETECTED"; } $paths = explode(DIRECTORY_SEPARATOR, $path); - if ($paths[count($paths)-3] == "Mftf") { + if (!isset($paths[count($paths)-3])) { + return "NO MODULE DETECTED"; + } elseif ($paths[count($paths)-3] == "Mftf") { // app/code/Magento/[Analytics]/Test/Mftf/Test/SomeText.xml return $paths[count($paths)-5]; } From ca7f802e74df22393ac472b0f5a93def50b9a78f Mon Sep 17 00:00:00 2001 From: KevinBKozan Date: Tue, 8 May 2018 08:48:45 -0500 Subject: [PATCH 031/111] MQE-812: Tests/Action Groups should infer order based on the top level argument - CR fixes --- .../ActionGroupMergedViaInsertAfter.txt | 6 +++--- .../ActionGroupMergedViaInsertBefore.txt | 6 +++--- .../MergeFunctionalActionGroup.xml | 12 +++++------ .../Test/Config/Dom.php | 20 +++++++++---------- 4 files changed, 21 insertions(+), 23 deletions(-) diff --git a/dev/tests/verification/Resources/ActionGroupMergedViaInsertAfter.txt b/dev/tests/verification/Resources/ActionGroupMergedViaInsertAfter.txt index 63565d7f8..7422cbc26 100644 --- a/dev/tests/verification/Resources/ActionGroupMergedViaInsertAfter.txt +++ b/dev/tests/verification/Resources/ActionGroupMergedViaInsertAfter.txt @@ -29,9 +29,9 @@ class ActionGroupMergedViaInsertAfterCest { $I->fillField("#foo", "foo"); $I->fillField("#bar", "bar"); - $I->click("#mergeOne"); - $I->click("#mergeTwo"); - $I->click("#mergeThree"); + $I->click("#foo2"); + $I->click("#bar2"); + $I->click("#baz2"); $I->fillField("#baz", "baz"); } } diff --git a/dev/tests/verification/Resources/ActionGroupMergedViaInsertBefore.txt b/dev/tests/verification/Resources/ActionGroupMergedViaInsertBefore.txt index f44e15b4e..4a7dcf172 100644 --- a/dev/tests/verification/Resources/ActionGroupMergedViaInsertBefore.txt +++ b/dev/tests/verification/Resources/ActionGroupMergedViaInsertBefore.txt @@ -28,9 +28,9 @@ class ActionGroupMergedViaInsertBeforeCest public function ActionGroupMergedViaInsertBefore(AcceptanceTester $I) { $I->fillField("#foo", "foo"); - $I->click("#mergeOne"); - $I->click("#mergeTwo"); - $I->click("#mergeThree"); + $I->click("#foo2"); + $I->click("#bar2"); + $I->click("#baz2"); $I->fillField("#bar", "bar"); $I->fillField("#baz", "baz"); } diff --git a/dev/tests/verification/TestModule/ActionGroup/MergeFunctionalActionGroup.xml b/dev/tests/verification/TestModule/ActionGroup/MergeFunctionalActionGroup.xml index e993395c6..760853896 100644 --- a/dev/tests/verification/TestModule/ActionGroup/MergeFunctionalActionGroup.xml +++ b/dev/tests/verification/TestModule/ActionGroup/MergeFunctionalActionGroup.xml @@ -15,14 +15,14 @@ - - - + + + - - - + + + diff --git a/src/Magento/FunctionalTestingFramework/Test/Config/Dom.php b/src/Magento/FunctionalTestingFramework/Test/Config/Dom.php index ea516dd22..fb7b0a386 100644 --- a/src/Magento/FunctionalTestingFramework/Test/Config/Dom.php +++ b/src/Magento/FunctionalTestingFramework/Test/Config/Dom.php @@ -123,20 +123,18 @@ protected function appendMergePointerToActions($testNode, $insertType, $insertKe } for ($i = 0; $i < $childNodes->length; $i++) { $currentNode = $childNodes->item($i); - if (!is_a($currentNode, \DOMElement::class)) { + if (!is_a($currentNode, \DOMElement::class) || !$currentNode->hasAttribute('stepKey')) { continue; } - if ($currentNode->hasAttribute('stepKey')) { - if ($currentNode->hasAttribute($insertType) && $testNode->hasAttribute($insertType)) { - $errorMsg = "Actions cannot have merge pointers if contained in tests that has a merge pointer."; - $errorMsg .= "\n\tstepKey: {$currentNode->getAttribute('stepKey')}\tin file: {$filename}"; - $exceptionCollector->addError($filename, $errorMsg); - } - $currentNode->setAttribute($actionInsertType, $previousStepKey); - $previousStepKey = $currentNode->getAttribute('stepKey'); - // All actions after the first need to insert AFTER. - $actionInsertType = ActionObject::MERGE_ACTION_ORDER_AFTER; + if ($currentNode->hasAttribute($insertType) && $testNode->hasAttribute($insertType)) { + $errorMsg = "Actions cannot have merge pointers if contained in tests that has a merge pointer."; + $errorMsg .= "\n\tstepKey: {$currentNode->getAttribute('stepKey')}\tin file: {$filename}"; + $exceptionCollector->addError($filename, $errorMsg); } + $currentNode->setAttribute($actionInsertType, $previousStepKey); + $previousStepKey = $currentNode->getAttribute('stepKey'); + // All actions after the first need to insert AFTER. + $actionInsertType = ActionObject::MERGE_ACTION_ORDER_AFTER; } } From c9fbac0ee15c2c3c19776ab62cf76001f920c7a1 Mon Sep 17 00:00:00 2001 From: KevinBKozan Date: Tue, 8 May 2018 09:44:08 -0500 Subject: [PATCH 032/111] MQE-812: Tests/Action Groups should infer order based on the top level argument - CR fix, data is now correct. --- .../TestModule/ActionGroup/MergeFunctionalActionGroup.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dev/tests/verification/TestModule/ActionGroup/MergeFunctionalActionGroup.xml b/dev/tests/verification/TestModule/ActionGroup/MergeFunctionalActionGroup.xml index 760853896..7d8585772 100644 --- a/dev/tests/verification/TestModule/ActionGroup/MergeFunctionalActionGroup.xml +++ b/dev/tests/verification/TestModule/ActionGroup/MergeFunctionalActionGroup.xml @@ -15,13 +15,13 @@ - + - + From 097ebe893975ef100e1b719734f5c8d42ad37725 Mon Sep 17 00:00:00 2001 From: Kevin Kozan Date: Tue, 8 May 2018 10:00:01 -0500 Subject: [PATCH 033/111] MQE-991: The second data entity replacement is not correct when there are two in the same action inside an action group - usecase fixed, added verification test around new case --- .../PersistedAndXmlEntityArguments.txt | 32 +++++++++++++++++++ .../ActionGroup/FunctionalActionGroup.xml | 7 ++++ .../Test/ActionGroupFunctionalTest.xml | 5 +++ .../Tests/ActionGroupGenerationTest.php | 11 +++++++ .../Test/Objects/ActionGroupObject.php | 8 +++-- 5 files changed, 61 insertions(+), 2 deletions(-) create mode 100644 dev/tests/verification/Resources/PersistedAndXmlEntityArguments.txt diff --git a/dev/tests/verification/Resources/PersistedAndXmlEntityArguments.txt b/dev/tests/verification/Resources/PersistedAndXmlEntityArguments.txt new file mode 100644 index 000000000..126e9fb0a --- /dev/null +++ b/dev/tests/verification/Resources/PersistedAndXmlEntityArguments.txt @@ -0,0 +1,32 @@ +seeInCurrentUrl("/" . $persistedInTest->getCreatedDataByName('urlKey') . ".html?___store=" . msq("uniqueData") . "John"); + } +} diff --git a/dev/tests/verification/TestModule/ActionGroup/FunctionalActionGroup.xml b/dev/tests/verification/TestModule/ActionGroup/FunctionalActionGroup.xml index 843c66739..342299804 100644 --- a/dev/tests/verification/TestModule/ActionGroup/FunctionalActionGroup.xml +++ b/dev/tests/verification/TestModule/ActionGroup/FunctionalActionGroup.xml @@ -50,4 +50,11 @@ + + + + + + + diff --git a/dev/tests/verification/TestModule/Test/ActionGroupFunctionalTest.xml b/dev/tests/verification/TestModule/Test/ActionGroupFunctionalTest.xml index ad372b838..6a0558628 100644 --- a/dev/tests/verification/TestModule/Test/ActionGroupFunctionalTest.xml +++ b/dev/tests/verification/TestModule/Test/ActionGroupFunctionalTest.xml @@ -182,4 +182,9 @@ + + + + + diff --git a/dev/tests/verification/Tests/ActionGroupGenerationTest.php b/dev/tests/verification/Tests/ActionGroupGenerationTest.php index a26e9ad87..1de24280b 100644 --- a/dev/tests/verification/Tests/ActionGroupGenerationTest.php +++ b/dev/tests/verification/Tests/ActionGroupGenerationTest.php @@ -118,4 +118,15 @@ public function testActionGroupWithNestedArgument() { $this->generateAndCompareTest('ActionGroupUsingNestedArgument'); } + + /** + * Test generation of a test referencing an action group that uses stepKey references (grabFrom/CreateData) + * + * @throws \Exception + * @throws \Magento\FunctionalTestingFramework\Exceptions\TestReferenceException + */ + public function testActionGroupWithPersistedAndXmlEntityArguments() + { + $this->generateAndCompareTest('PersistedAndXmlEntityArguments'); + } } diff --git a/src/Magento/FunctionalTestingFramework/Test/Objects/ActionGroupObject.php b/src/Magento/FunctionalTestingFramework/Test/Objects/ActionGroupObject.php index d1e8112e6..5c8b5bfb2 100644 --- a/src/Magento/FunctionalTestingFramework/Test/Objects/ActionGroupObject.php +++ b/src/Magento/FunctionalTestingFramework/Test/Objects/ActionGroupObject.php @@ -332,8 +332,12 @@ private function replacePersistedArgument($replacement, $attributeValue, $fullVa $fullReplacement = str_replace($variable, trim($replacement, '$'), trim($fullVariable, "'")); $newAttributeValue = str_replace($fullVariable, $scope . $fullReplacement . $scope, $newAttributeValue); } else { - $newAttributeValue = str_replace('{{', $scope, str_replace('}}', $scope, $newAttributeValue)); - $newAttributeValue = str_replace($variable, trim($replacement, '$'), $newAttributeValue); + $fullReplacement = str_replace($variable, trim($replacement, '$'), $fullVariable); + $newAttributeValue = str_replace( + '{{' . $fullVariable . '}}', + $scope . $fullReplacement . $scope, + $newAttributeValue + ); } return $newAttributeValue; From ec5d25a5eb8c1bc4cf44739d47218339602e9d9a Mon Sep 17 00:00:00 2001 From: KevinBKozan Date: Tue, 8 May 2018 11:08:43 -0500 Subject: [PATCH 034/111] MQE-980: [Annotations] tag defaults to module - CR Fixes. --- .../Test/Util/TestObjectExtractor.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Magento/FunctionalTestingFramework/Test/Util/TestObjectExtractor.php b/src/Magento/FunctionalTestingFramework/Test/Util/TestObjectExtractor.php index b200e7af3..9dc4b0652 100644 --- a/src/Magento/FunctionalTestingFramework/Test/Util/TestObjectExtractor.php +++ b/src/Magento/FunctionalTestingFramework/Test/Util/TestObjectExtractor.php @@ -138,7 +138,7 @@ private function extractModuleName($path) return "NO MODULE DETECTED"; } $paths = explode(DIRECTORY_SEPARATOR, $path); - if (!isset($paths[count($paths)-3])) { + if (count($paths) < 3) { return "NO MODULE DETECTED"; } elseif ($paths[count($paths)-3] == "Mftf") { // app/code/Magento/[Analytics]/Test/Mftf/Test/SomeText.xml From 3a07785e4dab5b83bf373a037f80ea7a5a1c654f Mon Sep 17 00:00:00 2001 From: KevinBKozan Date: Tue, 8 May 2018 14:03:11 -0500 Subject: [PATCH 035/111] MQE-980: [Annotations] tag defaults to module - Test Fix --- .../verification/Resources/PersistedAndXmlEntityArguments.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/dev/tests/verification/Resources/PersistedAndXmlEntityArguments.txt b/dev/tests/verification/Resources/PersistedAndXmlEntityArguments.txt index 126e9fb0a..9f60794e2 100644 --- a/dev/tests/verification/Resources/PersistedAndXmlEntityArguments.txt +++ b/dev/tests/verification/Resources/PersistedAndXmlEntityArguments.txt @@ -20,6 +20,7 @@ use Yandex\Allure\Adapter\Annotation\TestCaseId; class PersistedAndXmlEntityArgumentsCest { /** + * @Features({"TestModule"}) * @Parameter(name = "AcceptanceTester", value="$I") * @param AcceptanceTester $I * @return void From b28252d9980df74fb7375e4919308c41cda8f01d Mon Sep 17 00:00:00 2001 From: aljcalandra Date: Mon, 14 May 2018 16:08:00 -0500 Subject: [PATCH 036/111] MQE-812: Tests/Action Groups should infer order based on the top level argument - Updated Verification Resources to reflect inclusion of Features annotation --- .../verification/Resources/ActionGroupMergedViaInsertAfter.txt | 1 + .../verification/Resources/ActionGroupMergedViaInsertBefore.txt | 1 + dev/tests/verification/Resources/MergeMassViaInsertAfter.txt | 1 + dev/tests/verification/Resources/MergeMassViaInsertBefore.txt | 1 + 4 files changed, 4 insertions(+) diff --git a/dev/tests/verification/Resources/ActionGroupMergedViaInsertAfter.txt b/dev/tests/verification/Resources/ActionGroupMergedViaInsertAfter.txt index 7422cbc26..a62548ccc 100644 --- a/dev/tests/verification/Resources/ActionGroupMergedViaInsertAfter.txt +++ b/dev/tests/verification/Resources/ActionGroupMergedViaInsertAfter.txt @@ -20,6 +20,7 @@ use Yandex\Allure\Adapter\Annotation\TestCaseId; class ActionGroupMergedViaInsertAfterCest { /** + * @Features({"TestModule"}) * @Parameter(name = "AcceptanceTester", value="$I") * @param AcceptanceTester $I * @return void diff --git a/dev/tests/verification/Resources/ActionGroupMergedViaInsertBefore.txt b/dev/tests/verification/Resources/ActionGroupMergedViaInsertBefore.txt index 4a7dcf172..504ca8120 100644 --- a/dev/tests/verification/Resources/ActionGroupMergedViaInsertBefore.txt +++ b/dev/tests/verification/Resources/ActionGroupMergedViaInsertBefore.txt @@ -20,6 +20,7 @@ use Yandex\Allure\Adapter\Annotation\TestCaseId; class ActionGroupMergedViaInsertBeforeCest { /** + * @Features({"TestModule"}) * @Parameter(name = "AcceptanceTester", value="$I") * @param AcceptanceTester $I * @return void diff --git a/dev/tests/verification/Resources/MergeMassViaInsertAfter.txt b/dev/tests/verification/Resources/MergeMassViaInsertAfter.txt index f44f58e42..9ffc3d452 100644 --- a/dev/tests/verification/Resources/MergeMassViaInsertAfter.txt +++ b/dev/tests/verification/Resources/MergeMassViaInsertAfter.txt @@ -20,6 +20,7 @@ use Yandex\Allure\Adapter\Annotation\TestCaseId; class MergeMassViaInsertAfterCest { /** + * @Features({"TestModule"}) * @Parameter(name = "AcceptanceTester", value="$I") * @param AcceptanceTester $I * @return void diff --git a/dev/tests/verification/Resources/MergeMassViaInsertBefore.txt b/dev/tests/verification/Resources/MergeMassViaInsertBefore.txt index 2385eda39..a830c218c 100644 --- a/dev/tests/verification/Resources/MergeMassViaInsertBefore.txt +++ b/dev/tests/verification/Resources/MergeMassViaInsertBefore.txt @@ -20,6 +20,7 @@ use Yandex\Allure\Adapter\Annotation\TestCaseId; class MergeMassViaInsertBeforeCest { /** + * @Features({"TestModule"}) * @Parameter(name = "AcceptanceTester", value="$I") * @param AcceptanceTester $I * @return void From 1bedd14022a37e2c95db1e9a157fefd50bda81dc Mon Sep 17 00:00:00 2001 From: Alex Calandra Date: Tue, 15 May 2018 11:27:40 -0500 Subject: [PATCH 037/111] MQE-884: Show warning on generation when leave out .url attribute for amOnPage (#127) - Added Warning Line and unit test for said warning/conditional --- .../Test/Objects/ActionObjectTest.php | 36 +++++++++++++++++++ .../Test/Objects/ActionObject.php | 7 ++++ 2 files changed, 43 insertions(+) diff --git a/dev/tests/unit/Magento/FunctionalTestFramework/Test/Objects/ActionObjectTest.php b/dev/tests/unit/Magento/FunctionalTestFramework/Test/Objects/ActionObjectTest.php index b5aa8c4b1..8e3a1d579 100644 --- a/dev/tests/unit/Magento/FunctionalTestFramework/Test/Objects/ActionObjectTest.php +++ b/dev/tests/unit/Magento/FunctionalTestFramework/Test/Objects/ActionObjectTest.php @@ -191,6 +191,8 @@ public function testTimeoutFromElement() /** * {{PageObject.url}} should be replaced with someUrl.html + * + * @throws /Exception */ public function testResolveUrl() { @@ -213,6 +215,40 @@ public function testResolveUrl() $this->assertEquals($expected, $actionObject->getCustomActionAttributes()); } + /** + * {{PageObject}} should not be replaced and should elicit a warning in console + * + * @throws /Exception + */ + public function testResolveUrlWithNoAttribute() + { + // Set up mocks + $actionObject = new ActionObject('merge123', 'amOnPage', [ + 'url' => '{{PageObject}}' + ]); + $pageObject = new PageObject('PageObject', '/replacement/url.html', 'Test', [], false, "test"); + $pageObjectList = ["PageObject" => $pageObject]; + $instance = AspectMock::double( + PageObjectHandler::class, + ['getObject' => $pageObject, 'getAllObjects' => $pageObjectList] + )->make(); // bypass the private constructor + AspectMock::double(PageObjectHandler::class, ['getInstance' => $instance]); + + // Expect this warning to get generated + $this->expectOutputString( + "WARNING: Page url attribute not found for {{PageObject}} and is required for amOnPage." . PHP_EOL + ); + + // Call the method under test + $actionObject->resolveReferences(); + + // Verify + $expected = [ + 'url' => '{{PageObject}}' + ]; + $this->assertEquals($expected, $actionObject->getCustomActionAttributes()); + } + /** * {{PageObject.url(param)}} should be replaced */ diff --git a/src/Magento/FunctionalTestingFramework/Test/Objects/ActionObject.php b/src/Magento/FunctionalTestingFramework/Test/Objects/ActionObject.php index 87b8d0a12..e2bd106d1 100644 --- a/src/Magento/FunctionalTestingFramework/Test/Objects/ActionObject.php +++ b/src/Magento/FunctionalTestingFramework/Test/Objects/ActionObject.php @@ -372,6 +372,13 @@ private function resolveUrlReference() $replacement = $this->findAndReplaceReferences(PageObjectHandler::getInstance(), $url); if ($replacement) { $this->resolvedCustomAttributes[ActionObject::ACTION_ATTRIBUTE_URL] = $replacement; + $allPages = PageObjectHandler::getInstance()->getAllObjects(); + if ( + $replacement === $url + && array_key_exists(trim($url, "{}"), $allPages) + ) { + echo("WARNING: Page url attribute not found for ${url} and is required for $this->type." . PHP_EOL); + } } } From 22a1ef1002584555ffadb945016c65117d917248 Mon Sep 17 00:00:00 2001 From: Alex Calandra Date: Thu, 17 May 2018 08:42:34 -0500 Subject: [PATCH 038/111] MQE-992: Allow Tests and Action Groups to use inheritance (#128) * MQE-992: Allow Tests and Action Groups to use inheritance - Added Test Inheritance * MQE-992: Allow Tests and Action Groups to use inheritance - Added Action Group and moved inheritance to getObject calls in the handlers - Added index to actions - Updated unit test appropriately * MQE-992: Allow Tests and Action Groups to use inheritance - Fixed docstrings and use(s) - Moved some functionality for extending groups * MQE-992: Allow Tests and Action Groups to use inheritance - Added Verification Tests - Added Hooks to Test Extension - Updated a docstring for hook array * MQE-992: Allow Tests and Action Groups to use inheritance - Added ObjectExtension Unit Tests - Updated ArrayBuilders * MQE-992: Allow Tests and Action Groups to use inheritance - Added unit tests - Updated and split out verification tests - Updated docstrings - Added method for getting raw parsed steps to Test Object - Added Comments to ObjectExtension * MQE-992: Allow Tests and Action Groups to use inheritance - Updated class variable docstring - Removed unused verification resource - Added capture for output string for unit test * MQE-992: Allow Tests and Action Groups to use inheritance - Changed ObjectExtionsion to add Util - Added Constructor and updated methods to non-static - updated indexes * MQE-992: Allow Tests and Action Groups to use inheritance - Added support for Remove actions - Added more verification and unit tests - Refactored one long function * MQE-992: Allow Tests and Action Groups to use inheritance - Cleaned up remove function - Removed throw - Set stepKey differently for remove type actions * MQE-992: Allow Tests and Action Groups to use inheritance - Removed commentd line - moved creation of ObjectExtension object to class property * MQE-992: Allow Tests and Action Groups to use inheritance Moved stepKey assignment to Flat * MQE-992: Allow Tests and Action Groups to use inheritance - Removed unused constant * MQE-992: Allow Tests and Action Groups to use inheritance - Added functionality for removing actions from within test hooks * MQE-992: Allow Tests and Action Groups to use inheritance - Made change to order for actions in action groups when resolving actions * MQE-992: Allow Tests and Action Groups to use inheritance - Updated test for merged code * MQE-992: Allow Tests and Action Groups to use inheritance - Added warning instead of exception for non-existent parent tests * MQE-992: Allow Tests and Action Groups to use inheritance - Updated to only add skip to groups if it doesn't already exist * MQE-992: Allow Tests and Action Groups to use inheritance - Moved skipTest function to Object Extension Util --- .../Test/Handlers/TestObjectHandlerTest.php | 6 +- .../Test/Util/ObjectExtensionUtilTest.php | 318 ++++++++++++++++++ .../unit/Util/ActionGroupObjectBuilder.php | 22 +- dev/tests/unit/Util/TestDataArrayBuilder.php | 23 +- .../Resources/ActionGroupToExtend.txt | 34 ++ .../Resources/ChildExtendedTestAddHooks.txt | 63 ++++ .../Resources/ChildExtendedTestMerging.txt | 69 ++++ .../Resources/ChildExtendedTestNoParent.txt | 38 +++ .../ChildExtendedTestRemoveAction.txt | 63 ++++ .../ChildExtendedTestRemoveHookAction.txt | 63 ++++ .../Resources/ChildExtendedTestReplace.txt | 64 ++++ .../ChildExtendedTestReplaceHook.txt | 64 ++++ .../Resources/ExtendedActionGroup.txt | 37 ++ .../Resources/ExtendedRemoveActionGroup.txt | 32 ++ .../Resources/ParentExtendedTest.txt | 64 ++++ .../ActionGroup/BasicActionGroup.xml | 32 ++ .../TestModule/Test/ActionGroupTest.xml | 19 +- .../Test/ExtendedFunctionalTest.xml | 132 ++++++++ .../Tests/ActionGroupGenerationTest.php | 33 ++ .../Tests/ExtendedGenerationTest.php | 99 ++++++ .../Test/Config/Converter/Dom/Flat.php | 17 +- .../Handlers/ActionGroupObjectHandler.php | 32 +- .../Test/Handlers/TestObjectHandler.php | 35 +- .../Test/Objects/ActionGroupObject.php | 53 ++- .../Test/Objects/TestHookObject.php | 20 ++ .../Test/Objects/TestObject.php | 33 +- .../Test/Util/ActionGroupObjectExtractor.php | 8 +- .../Test/Util/ActionObjectExtractor.php | 2 +- .../Test/Util/ObjectExtensionUtil.php | 231 +++++++++++++ .../Test/Util/TestObjectExtractor.php | 7 +- .../Test/etc/actionGroupSchema.xsd | 1 + .../Test/etc/mergedTestSchema.xsd | 1 + 32 files changed, 1692 insertions(+), 23 deletions(-) create mode 100644 dev/tests/unit/Magento/FunctionalTestFramework/Test/Util/ObjectExtensionUtilTest.php create mode 100644 dev/tests/verification/Resources/ActionGroupToExtend.txt create mode 100644 dev/tests/verification/Resources/ChildExtendedTestAddHooks.txt create mode 100644 dev/tests/verification/Resources/ChildExtendedTestMerging.txt create mode 100644 dev/tests/verification/Resources/ChildExtendedTestNoParent.txt create mode 100644 dev/tests/verification/Resources/ChildExtendedTestRemoveAction.txt create mode 100644 dev/tests/verification/Resources/ChildExtendedTestRemoveHookAction.txt create mode 100644 dev/tests/verification/Resources/ChildExtendedTestReplace.txt create mode 100644 dev/tests/verification/Resources/ChildExtendedTestReplaceHook.txt create mode 100644 dev/tests/verification/Resources/ExtendedActionGroup.txt create mode 100644 dev/tests/verification/Resources/ExtendedRemoveActionGroup.txt create mode 100644 dev/tests/verification/Resources/ParentExtendedTest.txt create mode 100644 dev/tests/verification/TestModule/Test/ExtendedFunctionalTest.xml create mode 100644 dev/tests/verification/Tests/ExtendedGenerationTest.php create mode 100644 src/Magento/FunctionalTestingFramework/Test/Util/ObjectExtensionUtil.php diff --git a/dev/tests/unit/Magento/FunctionalTestFramework/Test/Handlers/TestObjectHandlerTest.php b/dev/tests/unit/Magento/FunctionalTestFramework/Test/Handlers/TestObjectHandlerTest.php index 860c8d3bb..54458cd3e 100644 --- a/dev/tests/unit/Magento/FunctionalTestFramework/Test/Handlers/TestObjectHandlerTest.php +++ b/dev/tests/unit/Magento/FunctionalTestFramework/Test/Handlers/TestObjectHandlerTest.php @@ -66,12 +66,12 @@ public function testGetTestObject() $expectedBeforeHookObject = new TestHookObject( TestObjectExtractor::TEST_BEFORE_HOOK, $testDataArrayBuilder->testName, - [$expectedBeforeActionObject] + ["testActionBefore" => $expectedBeforeActionObject] ); $expectedAfterHookObject = new TestHookObject( TestObjectExtractor::TEST_AFTER_HOOK, $testDataArrayBuilder->testName, - [$expectedAfterActionObject] + ["testActionAfter" => $expectedAfterActionObject] ); $expectedFailedHookObject = new TestHookObject( TestObjectExtractor::TEST_FAILED_HOOK, @@ -86,7 +86,7 @@ public function testGetTestObject() ); $expectedTestObject = new TestObject( $testDataArrayBuilder->testName, - [$expectedTestActionObject], + ["testActionInTest" => $expectedTestActionObject], [ 'features' => ['NO MODULE DETECTED'], 'group' => ['test'] diff --git a/dev/tests/unit/Magento/FunctionalTestFramework/Test/Util/ObjectExtensionUtilTest.php b/dev/tests/unit/Magento/FunctionalTestFramework/Test/Util/ObjectExtensionUtilTest.php new file mode 100644 index 000000000..ddddf6d4b --- /dev/null +++ b/dev/tests/unit/Magento/FunctionalTestFramework/Test/Util/ObjectExtensionUtilTest.php @@ -0,0 +1,318 @@ + ["nodeName" => "mockNode", "stepKey" => "mockStep"] + ]; + + $testDataArrayBuilder = new TestDataArrayBuilder(); + $mockSimpleTest = $testDataArrayBuilder + ->withName('simpleTest') + ->withTestActions($mockActions) + ->build(); + + $mockExtendedTest = $testDataArrayBuilder + ->withName('extendedTest') + ->withTestReference("simpleTest") + ->build(); + + $mockTestData = ['tests' => array_merge($mockSimpleTest, $mockExtendedTest)]; + $this->setMockTestOutput($mockTestData); + + $this->expectOutputString("Extending Test: simpleTest => extendedTest" . PHP_EOL); + + // parse and generate test object with mocked data + $testObject = TestObjectHandler::getInstance()->getObject('extendedTest'); + + // assert that expected test is generated + $this->assertEquals($testObject->getParentName(), "simpleTest"); + $this->assertArrayHasKey("mockStep", $testObject->getOrderedActions()); + } + + /** + * Tests generating a test that extends another test + * @throws \Exception + */ + public function testGenerateExtendedWithHooks() + { + $mockBeforeHooks = [ + "beforeHookAction" => ["nodeName" => "mockNodeBefore", "stepKey" => "mockStepBefore"] + ]; + $mockAfterHooks = [ + "afterHookAction" => ["nodeName" => "mockNodeAfter", "stepKey" => "mockStepAfter"] + ]; + + $testDataArrayBuilder = new TestDataArrayBuilder(); + $mockSimpleTest = $testDataArrayBuilder + ->withName('simpleTest') + ->withBeforeHook($mockBeforeHooks) + ->withAfterHook($mockAfterHooks) + ->build(); + + $mockExtendedTest = $testDataArrayBuilder + ->withName('extendedTest') + ->withTestReference("simpleTest") + ->build(); + + $mockTestData = ['tests' => array_merge($mockSimpleTest, $mockExtendedTest)]; + $this->setMockTestOutput($mockTestData); + + $this->expectOutputString("Extending Test: simpleTest => extendedTest" . PHP_EOL); + + // parse and generate test object with mocked data + $testObject = TestObjectHandler::getInstance()->getObject('extendedTest'); + + // assert that expected test is generated + $this->assertEquals($testObject->getParentName(), "simpleTest"); + $this->assertArrayHasKey("mockStepBefore", $testObject->getHooks()['before']->getActions()); + $this->assertArrayHasKey("mockStepAfter", $testObject->getHooks()['after']->getActions()); + } + + /** + * Tests generating a test that extends another test + * @throws \Exception + */ + public function testExtendedTestNoParent() + { + $testDataArrayBuilder = new TestDataArrayBuilder(); + $mockExtendedTest = $testDataArrayBuilder + ->withName('extendedTest') + ->withTestReference("simpleTest") + ->build(); + + $mockTestData = ['tests' => array_merge($mockExtendedTest)]; + $this->setMockTestOutput($mockTestData); + + $this->expectOutputString("Parent Test simpleTest not defined for Test extendedTest. Skipping Test." . PHP_EOL); + + // parse and generate test object with mocked data + TestObjectHandler::getInstance()->getObject('extendedTest'); + } + + /** + * Tests generating a test that extends another test + * @throws \Exception + */ + public function testExtendingExtendedTest() + { + $testDataArrayBuilder = new TestDataArrayBuilder(); + $mockParentTest = $testDataArrayBuilder + ->withName('anotherTest') + ->withTestActions() + ->build(); + + $mockSimpleTest = $testDataArrayBuilder + ->withName('simpleTest') + ->withTestActions() + ->withTestReference("anotherTest") + ->build(); + + $mockExtendedTest = $testDataArrayBuilder + ->withName('extendedTest') + ->withTestReference("simpleTest") + ->build(); + + $mockTestData = ['tests' => array_merge($mockParentTest, $mockSimpleTest, $mockExtendedTest)]; + $this->setMockTestOutput($mockTestData); + + $this->expectOutputString("Extending Test: anotherTest => simpleTest" . PHP_EOL); + $this->expectExceptionMessage("Cannot extend a test that already extends another test. Test: simpleTest"); + + // parse and generate test object with mocked data + TestObjectHandler::getInstance()->getObject('extendedTest'); + } + + /** + * Tests generating an action group that extends another action group + * @throws \Exception + */ + public function testGenerateExtendedActionGroup() + { + $mockSimpleActionGroup = [ + "nodeName" => "actionGroup", + "name" => "mockSimpleActionGroup", + "filename" => "someFile", + "commentHere" => [ + "nodeName" => "comment", + "selector" => "selector", + "stepKey" => "commentHere" + ], + "parentComment" => [ + "nodeName" => "comment", + "selector" => "parentSelector", + "stepKey" => "parentComment" + ], + ]; + + $mockExtendedActionGroup = [ + "nodeName" => "actionGroup", + "name" => "mockExtendedActionGroup", + "filename" => "someFile", + "extends" => "mockSimpleActionGroup", + "commentHere" => [ + "nodeName" => "comment", + "selector" => "otherSelector", + "stepKey" => "commentHere" + ], + ]; + + $mockActionGroupData = [ + 'actionGroups' => [ + 'mockSimpleActionGroup' => $mockSimpleActionGroup, + 'mockExtendedActionGroup' => $mockExtendedActionGroup + ] + ]; + $this->setMockTestOutput(null, $mockActionGroupData); + + $this->expectOutputString("Extending Action Group: mockSimpleActionGroup => mockExtendedActionGroup" . PHP_EOL); + + // parse and generate test object with mocked data + $actionGroupObject = ActionGroupObjectHandler::getInstance()->getObject('mockExtendedActionGroup'); + + // assert that expected test is generated + $this->assertEquals("mockSimpleActionGroup", $actionGroupObject->getParentName()); + $actions = $actionGroupObject->getActions(); + $this->assertEquals("otherSelector", $actions["commentHere"]->getCustomActionAttributes()["selector"]); + $this->assertEquals("parentSelector", $actions["parentComment"]->getCustomActionAttributes()["selector"]); + } + + /** + * Tests generating an action group that extends an action group that does not exist + * @throws \Exception + */ + public function testGenerateExtendedActionGroupNoParent() + { + $mockExtendedActionGroup = [ + "nodeName" => "actionGroup", + "name" => "mockSimpleActionGroup", + "filename" => "someFile", + "extends" => "mockSimpleActionGroup", + "commentHere" => [ + "nodeName" => "comment", + "selector" => "otherSelector", + "stepKey" => "commentHere" + ], + ]; + + $mockActionGroupData = [ + 'actionGroups' => [ + 'mockExtendedActionGroup' => $mockExtendedActionGroup + ] + ]; + $this->setMockTestOutput(null, $mockActionGroupData); + + $this->expectExceptionMessage( + "Parent Action Group mockSimpleActionGroup not defined for Test " . $mockExtendedActionGroup['extends'] + ); + + // parse and generate test object with mocked data + ActionGroupObjectHandler::getInstance()->getObject('mockExtendedActionGroup'); + } + + /** + * Tests generating an action group that extends another action group that is already extended + * @throws \Exception + */ + public function testExtendingExtendedActionGroup() + { + $mockParentActionGroup = [ + "nodeName" => "actionGroup", + "name" => "mockParentActionGroup", + "filename" => "someFile" + ]; + + $mockSimpleActionGroup = [ + "nodeName" => "actionGroup", + "name" => "mockSimpleActionGroup", + "filename" => "someFile", + "extends" => "mockParentActionGroup", + ]; + + $mockExtendedActionGroup = [ + "nodeName" => "actionGroup", + "name" => "mockSimpleActionGroup", + "filename" => "someFile", + "extends" => "mockSimpleActionGroup", + ]; + + $mockActionGroupData = [ + 'actionGroups' => [ + 'mockParentActionGroup' => $mockParentActionGroup, + 'mockSimpleActionGroup' => $mockSimpleActionGroup, + 'mockExtendedActionGroup' => $mockExtendedActionGroup + ] + ]; + $this->setMockTestOutput(null, $mockActionGroupData); + + $this->expectOutputString("Extending Action Group: mockParentActionGroup => mockSimpleActionGroup" . PHP_EOL); + $this->expectExceptionMessage( + "Cannot extend an action group that already extends another action group. " . $mockSimpleActionGroup['name'] + ); + + // parse and generate test object with mocked data + ActionGroupObjectHandler::getInstance()->getObject('mockExtendedActionGroup'); + } + + /** + * Function used to set mock for parser return and force init method to run between tests. + * + * @param array $testData + * @throws \Exception + */ + private function setMockTestOutput($testData = null, $actionGroupData = null) + { + // clear test object handler value to inject parsed content + $property = new \ReflectionProperty(TestObjectHandler::class, 'testObjectHandler'); + $property->setAccessible(true); + $property->setValue(null); + + // clear test object handler value to inject parsed content + $property = new \ReflectionProperty(ActionGroupObjectHandler::class, 'ACTION_GROUP_OBJECT_HANDLER'); + $property->setAccessible(true); + $property->setValue(null); + + $mockDataParser = AspectMock::double(TestDataParser::class, ['readTestData' => $testData])->make(); + $mockActionGroupParser = AspectMock::double( + ActionGroupDataParser::class, + ['readActionGroupData' => $actionGroupData] + )->make(); + $instance = AspectMock::double( + ObjectManager::class, + ['create' => function ($clazz) use ( + $mockDataParser, + $mockActionGroupParser + ) { + if ($clazz == TestDataParser::class) { + return $mockDataParser; + } + if ($clazz == ActionGroupDataParser::class) { + return $mockActionGroupParser; + } + }] + )->make(); + // bypass the private constructor + AspectMock::double(ObjectManagerFactory::class, ['getObjectManager' => $instance]); + } +} diff --git a/dev/tests/unit/Util/ActionGroupObjectBuilder.php b/dev/tests/unit/Util/ActionGroupObjectBuilder.php index 4844776d5..f97d0465d 100644 --- a/dev/tests/unit/Util/ActionGroupObjectBuilder.php +++ b/dev/tests/unit/Util/ActionGroupObjectBuilder.php @@ -34,6 +34,13 @@ class ActionGroupObjectBuilder */ private $arguments = []; + /** + * Action Group Object Builder default name + * + * @var string + */ + private $extends = null; + /** * Setter for the Action Group Object name * @@ -70,6 +77,18 @@ public function withActionObjects($actionObjs) return $this; } + /** + * Setter for the Action Group Object extended objects + * + * @param string $extendedActionGroup + * @return ActionGroupObjectBuilder + */ + public function withExtendedAction($extendedActionGroup) + { + $this->extends = $extendedActionGroup; + return $this; + } + /** * ActionGroupObjectBuilder constructor. */ @@ -90,7 +109,8 @@ public function build() return new ActionGroupObject( $this->name, $this->arguments, - $this->actionObjects + $this->actionObjects, + $this->extends ); } } diff --git a/dev/tests/unit/Util/TestDataArrayBuilder.php b/dev/tests/unit/Util/TestDataArrayBuilder.php index 2dc739b94..a8483c26f 100644 --- a/dev/tests/unit/Util/TestDataArrayBuilder.php +++ b/dev/tests/unit/Util/TestDataArrayBuilder.php @@ -86,6 +86,11 @@ class TestDataArrayBuilder */ private $testActions = []; + /** + * @var array + */ + private $testReference = null; + /** * @param $name * @return $this @@ -212,6 +217,21 @@ public function withFileName($filename = null) return $this; } + /** + * Add test reference passed in by arg (or default if no arg) + * + * @param string $reference + * @return $this + */ + public function withTestReference($reference = null) + { + if ($reference != null) { + $this->testReference = $reference; + } + + return $this; + } + /** * Output the resulting test data array based on parameters set in the object * @@ -227,7 +247,8 @@ public function build() TestObjectExtractor::TEST_BEFORE_HOOK => $this->beforeHook, TestObjectExtractor::TEST_AFTER_HOOK => $this->afterHook, TestObjectExtractor::TEST_FAILED_HOOK => $this->failedHook, - "filename" => $this->filename + "filename" => $this->filename, + "extends" => $this->testReference ], $this->testActions )]; diff --git a/dev/tests/verification/Resources/ActionGroupToExtend.txt b/dev/tests/verification/Resources/ActionGroupToExtend.txt new file mode 100644 index 000000000..bd077636d --- /dev/null +++ b/dev/tests/verification/Resources/ActionGroupToExtend.txt @@ -0,0 +1,34 @@ +grabMultiple("selector"); + $I->assertCount(99, $grabProductsActionGroup); + } +} diff --git a/dev/tests/verification/Resources/ChildExtendedTestAddHooks.txt b/dev/tests/verification/Resources/ChildExtendedTestAddHooks.txt new file mode 100644 index 000000000..3b7015529 --- /dev/null +++ b/dev/tests/verification/Resources/ChildExtendedTestAddHooks.txt @@ -0,0 +1,63 @@ +amOnPage("/beforeUrl"); + } + + /** + * @param AcceptanceTester $I + * @throws \Exception + */ + public function _after(AcceptanceTester $I) + { + $I->amOnPage("/afterUrl"); + } + + /** + * @param AcceptanceTester $I + * @throws \Exception + */ + public function _failed(AcceptanceTester $I) + { + $I->saveScreenshot(); + } + + /** + * @Severity(level = SeverityLevel::MINOR) + * @Features({"TestModule"}) + * @Stories({"Parent"}) + * @Parameter(name = "AcceptanceTester", value="$I") + * @param AcceptanceTester $I + * @return void + * @throws \Exception + */ + public function ChildExtendedTestAddHooks(AcceptanceTester $I) + { + } +} diff --git a/dev/tests/verification/Resources/ChildExtendedTestMerging.txt b/dev/tests/verification/Resources/ChildExtendedTestMerging.txt new file mode 100644 index 000000000..90943edfc --- /dev/null +++ b/dev/tests/verification/Resources/ChildExtendedTestMerging.txt @@ -0,0 +1,69 @@ +amOnPage("/firstUrl"); + $I->amOnPage("/beforeUrl"); + $I->amOnPage("/lastUrl"); + } + + /** + * @param AcceptanceTester $I + * @throws \Exception + */ + public function _after(AcceptanceTester $I) + { + $I->amOnPage("/afterUrl"); + } + + /** + * @param AcceptanceTester $I + * @throws \Exception + */ + public function _failed(AcceptanceTester $I) + { + $I->saveScreenshot(); + } + + /** + * @Severity(level = SeverityLevel::TRIVIAL) + * @Features({"TestModule"}) + * @Stories({"Child"}) + * @Parameter(name = "AcceptanceTester", value="$I") + * @param AcceptanceTester $I + * @return void + * @throws \Exception + */ + public function ChildExtendedTestMerging(AcceptanceTester $I) + { + $I->comment("Before Comment"); + $I->comment("Parent Comment"); + $I->comment("After Comment"); + $I->comment("Last Comment"); + } +} diff --git a/dev/tests/verification/Resources/ChildExtendedTestNoParent.txt b/dev/tests/verification/Resources/ChildExtendedTestNoParent.txt new file mode 100644 index 000000000..007db6bf5 --- /dev/null +++ b/dev/tests/verification/Resources/ChildExtendedTestNoParent.txt @@ -0,0 +1,38 @@ +skip("This test is skipped"); + } +} diff --git a/dev/tests/verification/Resources/ChildExtendedTestRemoveAction.txt b/dev/tests/verification/Resources/ChildExtendedTestRemoveAction.txt new file mode 100644 index 000000000..a9ef163b4 --- /dev/null +++ b/dev/tests/verification/Resources/ChildExtendedTestRemoveAction.txt @@ -0,0 +1,63 @@ +amOnPage("/beforeUrl"); + } + + /** + * @param AcceptanceTester $I + * @throws \Exception + */ + public function _after(AcceptanceTester $I) + { + $I->amOnPage("/afterUrl"); + } + + /** + * @param AcceptanceTester $I + * @throws \Exception + */ + public function _failed(AcceptanceTester $I) + { + $I->saveScreenshot(); + } + + /** + * @Severity(level = SeverityLevel::CRITICAL) + * @Features({"TestModule"}) + * @Stories({"Child"}) + * @Parameter(name = "AcceptanceTester", value="$I") + * @param AcceptanceTester $I + * @return void + * @throws \Exception + */ + public function ChildExtendedTestRemoveAction(AcceptanceTester $I) + { + } +} diff --git a/dev/tests/verification/Resources/ChildExtendedTestRemoveHookAction.txt b/dev/tests/verification/Resources/ChildExtendedTestRemoveHookAction.txt new file mode 100644 index 000000000..4a778a9fc --- /dev/null +++ b/dev/tests/verification/Resources/ChildExtendedTestRemoveHookAction.txt @@ -0,0 +1,63 @@ +amOnPage("/afterUrl"); + } + + /** + * @param AcceptanceTester $I + * @throws \Exception + */ + public function _failed(AcceptanceTester $I) + { + $I->saveScreenshot(); + } + + /** + * @Severity(level = SeverityLevel::CRITICAL) + * @Features({"TestModule"}) + * @Stories({"Child"}) + * @Parameter(name = "AcceptanceTester", value="$I") + * @param AcceptanceTester $I + * @return void + * @throws \Exception + */ + public function ChildExtendedTestRemoveHookAction(AcceptanceTester $I) + { + $I->comment("Parent Comment"); + } +} diff --git a/dev/tests/verification/Resources/ChildExtendedTestReplace.txt b/dev/tests/verification/Resources/ChildExtendedTestReplace.txt new file mode 100644 index 000000000..b41a22323 --- /dev/null +++ b/dev/tests/verification/Resources/ChildExtendedTestReplace.txt @@ -0,0 +1,64 @@ +amOnPage("/beforeUrl"); + } + + /** + * @param AcceptanceTester $I + * @throws \Exception + */ + public function _after(AcceptanceTester $I) + { + $I->amOnPage("/afterUrl"); + } + + /** + * @param AcceptanceTester $I + * @throws \Exception + */ + public function _failed(AcceptanceTester $I) + { + $I->saveScreenshot(); + } + + /** + * @Severity(level = SeverityLevel::TRIVIAL) + * @Features({"TestModule"}) + * @Stories({"Child"}) + * @Parameter(name = "AcceptanceTester", value="$I") + * @param AcceptanceTester $I + * @return void + * @throws \Exception + */ + public function ChildExtendedTestReplace(AcceptanceTester $I) + { + $I->comment("Different Input"); + } +} diff --git a/dev/tests/verification/Resources/ChildExtendedTestReplaceHook.txt b/dev/tests/verification/Resources/ChildExtendedTestReplaceHook.txt new file mode 100644 index 000000000..384aa8d5f --- /dev/null +++ b/dev/tests/verification/Resources/ChildExtendedTestReplaceHook.txt @@ -0,0 +1,64 @@ +amOnPage("/slightlyDifferentBeforeUrl"); + } + + /** + * @param AcceptanceTester $I + * @throws \Exception + */ + public function _after(AcceptanceTester $I) + { + $I->amOnPage("/afterUrl"); + } + + /** + * @param AcceptanceTester $I + * @throws \Exception + */ + public function _failed(AcceptanceTester $I) + { + $I->saveScreenshot(); + } + + /** + * @Severity(level = SeverityLevel::TRIVIAL) + * @Features({"TestModule"}) + * @Stories({"Child"}) + * @Parameter(name = "AcceptanceTester", value="$I") + * @param AcceptanceTester $I + * @return void + * @throws \Exception + */ + public function ChildExtendedTestReplaceHook(AcceptanceTester $I) + { + $I->comment("Parent Comment"); + } +} diff --git a/dev/tests/verification/Resources/ExtendedActionGroup.txt b/dev/tests/verification/Resources/ExtendedActionGroup.txt new file mode 100644 index 000000000..c89ce4e76 --- /dev/null +++ b/dev/tests/verification/Resources/ExtendedActionGroup.txt @@ -0,0 +1,37 @@ +comment("New Input Before"); + $grabProductsActionGroup = $I->grabMultiple("notASelector"); + $I->comment("New Input After"); + $I->assertCount(99, $grabProductsActionGroup); + $I->assertCount(8000, $grabProductsActionGroup); + } +} diff --git a/dev/tests/verification/Resources/ExtendedRemoveActionGroup.txt b/dev/tests/verification/Resources/ExtendedRemoveActionGroup.txt new file mode 100644 index 000000000..6539bb8cc --- /dev/null +++ b/dev/tests/verification/Resources/ExtendedRemoveActionGroup.txt @@ -0,0 +1,32 @@ +amOnPage("/beforeUrl"); + } + + /** + * @param AcceptanceTester $I + * @throws \Exception + */ + public function _after(AcceptanceTester $I) + { + $I->amOnPage("/afterUrl"); + } + + /** + * @param AcceptanceTester $I + * @throws \Exception + */ + public function _failed(AcceptanceTester $I) + { + $I->saveScreenshot(); + } + + /** + * @Severity(level = SeverityLevel::MINOR) + * @Features({"TestModule"}) + * @Stories({"Parent"}) + * @Parameter(name = "AcceptanceTester", value="$I") + * @param AcceptanceTester $I + * @return void + * @throws \Exception + */ + public function ParentExtendedTest(AcceptanceTester $I) + { + $I->comment("Parent Comment"); + } +} diff --git a/dev/tests/verification/TestModule/ActionGroup/BasicActionGroup.xml b/dev/tests/verification/TestModule/ActionGroup/BasicActionGroup.xml index 94e7a6b60..b53aa9db8 100644 --- a/dev/tests/verification/TestModule/ActionGroup/BasicActionGroup.xml +++ b/dev/tests/verification/TestModule/ActionGroup/BasicActionGroup.xml @@ -59,4 +59,36 @@ grabProducts + + + + + + + + {{count}} + grabProducts + + + + + + + + + + + + {{otherCount}} + grabProducts + + + + + + + + + + diff --git a/dev/tests/verification/TestModule/Test/ActionGroupTest.xml b/dev/tests/verification/TestModule/Test/ActionGroupTest.xml index 94b9e74dd..b784ce8f3 100644 --- a/dev/tests/verification/TestModule/Test/ActionGroupTest.xml +++ b/dev/tests/verification/TestModule/Test/ActionGroupTest.xml @@ -116,8 +116,25 @@ - + + + + + + + + + + + + + + + + + + diff --git a/dev/tests/verification/TestModule/Test/ExtendedFunctionalTest.xml b/dev/tests/verification/TestModule/Test/ExtendedFunctionalTest.xml new file mode 100644 index 000000000..dcf929188 --- /dev/null +++ b/dev/tests/verification/TestModule/Test/ExtendedFunctionalTest.xml @@ -0,0 +1,132 @@ + + + + + + + + + <group value="Parent"/> + <features value="Parent"/> + <stories value="Parent"/> + </annotations> + <before> + <amOnPage url="/beforeUrl" stepKey="beforeAmOnPageKey"/> + </before> + <after> + <amOnPage url="/afterUrl" stepKey="afterAmOnPageKey"/> + </after> + <comment stepKey="basicCommentWithNoData" userInput="Parent Comment"/> + </test> + + <test name="ChildExtendedTestReplace" extends="ParentExtendedTest"> + <annotations> + <severity value="MINOR"/> + <title value="Child"/> + <group value="Child"/> + <features value="Child"/> + <stories value="Child"/> + </annotations> + <comment stepKey="basicCommentWithNoData" userInput="Different Input"/> + </test> + + <test name="ChildExtendedTestReplaceHook" extends="ParentExtendedTest"> + <annotations> + <severity value="MINOR"/> + <title value="Child"/> + <group value="Child"/> + <features value="Child"/> + <stories value="Child"/> + </annotations> + <before> + <amOnPage url="/slightlyDifferentBeforeUrl" stepKey="beforeAmOnPageKey"/> + </before> + </test> + + <test name="ChildExtendedTestMerging" extends="ParentExtendedTest"> + <annotations> + <severity value="MINOR"/> + <title value="Child"/> + <group value="Child"/> + <features value="Child"/> + <stories value="Child"/> + </annotations> + <before> + <amOnPage url="/firstUrl" stepKey="firstBeforeAmOnPageKey" before="beforeAmOnPageKey"/> + <amOnPage url="/lastUrl" stepKey="lastBefore" after="beforeAmOnPageKey"/> + </before> + <comment stepKey="lastStepKey" userInput="Last Comment"/> + <comment stepKey="beforeBasicCommentWithNoData" userInput="Before Comment" before="basicCommentWithNoData"/> + <comment stepKey="afterBasicCommentWithNoData" userInput="After Comment" after="basicCommentWithNoData"/> + </test> + + <test name="ChildExtendedTestRemoveAction" extends="ParentExtendedTest"> + <annotations> + <severity value="CRITICAL"/> + <title value="Child"/> + <group value="Child"/> + <features value="Child"/> + <stories value="Child"/> + </annotations> + <remove keyForRemoval="basicCommentWithNoData"/> + </test> + + <test name="ParentExtendedTestNoHooks"> + <annotations> + <severity value="AVERAGE"/> + <title value="Parent"/> + <group value="Parent"/> + <features value="Parent"/> + <stories value="Parent"/> + </annotations> + <comment stepKey="basicCommentWithNoData" userInput="Parent Comment"/> + </test> + + <test name="ChildExtendedTestAddHooks"> + <annotations> + <severity value="AVERAGE"/> + <title value="Parent"/> + <group value="Parent"/> + <features value="Parent"/> + <stories value="Parent"/> + </annotations> + <before> + <amOnPage url="/beforeUrl" stepKey="beforeAmOnPageKey"/> + </before> + <after> + <amOnPage url="/afterUrl" stepKey="afterAmOnPageKey"/> + </after> + </test> + + <test name="ChildExtendedTestRemoveHookAction" extends="ParentExtendedTest"> + <annotations> + <severity value="CRITICAL"/> + <title value="Child"/> + <group value="Child"/> + <features value="Child"/> + <stories value="Child"/> + </annotations> + <before> + <remove keyForRemoval="beforeAmOnPageKey"/> + </before> + </test> + + <test name="ChildExtendedTestNoParent" extends="ThisTestDoesNotExist"> + <annotations> + <severity value="CRITICAL"/> + <title value="Child"/> + <group value="Child"/> + <features value="Child"/> + <stories value="Child"/> + </annotations> + <before> + <remove keyForRemoval="beforeAmOnPageKey"/> + </before> + </test> +</tests> \ No newline at end of file diff --git a/dev/tests/verification/Tests/ActionGroupGenerationTest.php b/dev/tests/verification/Tests/ActionGroupGenerationTest.php index 1de24280b..f7ce2b3fc 100644 --- a/dev/tests/verification/Tests/ActionGroupGenerationTest.php +++ b/dev/tests/verification/Tests/ActionGroupGenerationTest.php @@ -129,4 +129,37 @@ public function testActionGroupWithPersistedAndXmlEntityArguments() { $this->generateAndCompareTest('PersistedAndXmlEntityArguments'); } + + /** + * Test generation of a test referencing an action group which is referenced by another action group + * + * @throws \Exception + * @throws \Magento\FunctionalTestingFramework\Exceptions\TestReferenceException + */ + public function testActionGroupToExtend() + { + $this->generateAndCompareTest('ActionGroupToExtend'); + } + + /** + * Test generation of a test referencing an action group that references another action group + * + * @throws \Exception + * @throws \Magento\FunctionalTestingFramework\Exceptions\TestReferenceException + */ + public function testExtendedActionGroup() + { + $this->generateAndCompareTest('ExtendedActionGroup'); + } + + /** + * Test generation of a test referencing an action group that references another action group but removes an action + * + * @throws \Exception + * @throws \Magento\FunctionalTestingFramework\Exceptions\TestReferenceException + */ + public function testExtendedRemoveActionGroup() + { + $this->generateAndCompareTest('ExtendedRemoveActionGroup'); + } } diff --git a/dev/tests/verification/Tests/ExtendedGenerationTest.php b/dev/tests/verification/Tests/ExtendedGenerationTest.php new file mode 100644 index 000000000..d0c9926d0 --- /dev/null +++ b/dev/tests/verification/Tests/ExtendedGenerationTest.php @@ -0,0 +1,99 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +namespace tests\verification\Tests; + +use tests\util\MftfTestCase; + +class ExtendedGenerationTest extends MftfTestCase +{ + /** + * Tests flat generation of a test that is referenced by another test + * + * @throws \Exception + * @throws \Magento\FunctionalTestingFramework\Exceptions\TestReferenceException + */ + public function testExtendedParentTestGeneration() + { + $this->generateAndCompareTest('ParentExtendedTest'); + } + + /** + * Tests generation of test that extends based on another test when replacing actions + * + * @throws \Exception + * @throws \Magento\FunctionalTestingFramework\Exceptions\TestReferenceException + */ + public function testExtendedTestGenerationReplaceStepKey() + { + $this->generateAndCompareTest('ChildExtendedTestReplace'); + } + + /** + * Tests generation of test that extends based on another test when replacing actions in hooks + * + * @throws \Exception + * @throws \Magento\FunctionalTestingFramework\Exceptions\TestReferenceException + */ + public function testExtendedTestGenerationReplaceHook() + { + $this->generateAndCompareTest('ChildExtendedTestReplaceHook'); + } + + /** + * Tests generation of test that extends based on another test when merging actions + * + * @throws \Exception + * @throws \Magento\FunctionalTestingFramework\Exceptions\TestReferenceException + */ + public function testExtendedTestGenerationMergeActions() + { + $this->generateAndCompareTest('ChildExtendedTestMerging'); + } + + /** + * Tests generation of test that extends based on another test when adding hooks + * + * @throws \Exception + * @throws \Magento\FunctionalTestingFramework\Exceptions\TestReferenceException + */ + public function testExtendedTestGenerationAddHooks() + { + $this->generateAndCompareTest('ChildExtendedTestAddHooks'); + } + + /** + * Tests generation of test that extends based on another test when removing an action + * + * @throws \Exception + * @throws \Magento\FunctionalTestingFramework\Exceptions\TestReferenceException + */ + public function testExtendedTestGenerationRemoveAction() + { + $this->generateAndCompareTest('ChildExtendedTestRemoveAction'); + } + + /** + * Tests generation of test that extends based on another test when removing an action + * + * @throws \Exception + * @throws \Magento\FunctionalTestingFramework\Exceptions\TestReferenceException + */ + public function testExtendedTestGenerationRemoveHookAction() + { + $this->generateAndCompareTest('ChildExtendedTestRemoveHookAction'); + } + + /** + * Tests generation of test that attemps to extend a test that doesn't exist + * + * @throws \Exception + * @throws \Magento\FunctionalTestingFramework\Exceptions\TestReferenceException + */ + public function testExtendedTestGenerationNoParent() + { + $this->generateAndCompareTest('ChildExtendedTestNoParent'); + } +} diff --git a/src/Magento/FunctionalTestingFramework/Test/Config/Converter/Dom/Flat.php b/src/Magento/FunctionalTestingFramework/Test/Config/Converter/Dom/Flat.php index 254cdcc69..db4fec886 100644 --- a/src/Magento/FunctionalTestingFramework/Test/Config/Converter/Dom/Flat.php +++ b/src/Magento/FunctionalTestingFramework/Test/Config/Converter/Dom/Flat.php @@ -16,6 +16,8 @@ class Flat implements ConverterInterface { const REMOVE_ACTION = 'remove'; const REMOVE_KEY_ATTRIBUTE = 'keyForRemoval'; + const EXTENDS_ATTRIBUTE = 'extends'; + const TEST_HOOKS = ['before', 'after']; /** * Array node configuration. @@ -87,8 +89,19 @@ public function convertXml(\DOMNode $source, $basePath = '') } if ($nodeName == self::REMOVE_ACTION) { - unset($value[$node->getAttribute(self::REMOVE_KEY_ATTRIBUTE)]); - continue; + // Check to see if the test extends for this remove action + $parentHookExtends = in_array($node->parentNode->nodeName, self::TEST_HOOKS) + && !empty($node->parentNode->parentNode->getAttribute('extends')); + $test_extends = $parentHookExtends || !empty($node->parentNode->getAttribute('extends')); + + // If the test does extend, don't remove the remove action and set the stepkey + if ($test_extends) { + $keyForRemoval = $node->getAttribute('keyForRemoval'); + $node->setAttribute('stepKey', $keyForRemoval); + } else { + unset($value[$node->getAttribute(self::REMOVE_KEY_ATTRIBUTE)]); + continue; + } } $nodeData = $this->convertXml($node, $nodePath); diff --git a/src/Magento/FunctionalTestingFramework/Test/Handlers/ActionGroupObjectHandler.php b/src/Magento/FunctionalTestingFramework/Test/Handlers/ActionGroupObjectHandler.php index 6cb31770f..994467bd3 100644 --- a/src/Magento/FunctionalTestingFramework/Test/Handlers/ActionGroupObjectHandler.php +++ b/src/Magento/FunctionalTestingFramework/Test/Handlers/ActionGroupObjectHandler.php @@ -11,6 +11,7 @@ use Magento\FunctionalTestingFramework\Test\Objects\TestObject; use Magento\FunctionalTestingFramework\Test\Parsers\ActionGroupDataParser; use Magento\FunctionalTestingFramework\Test\Util\ActionGroupObjectExtractor; +use Magento\FunctionalTestingFramework\Test\Util\ObjectExtensionUtil; /** * Class ActionGroupObjectHandler @@ -35,6 +36,13 @@ class ActionGroupObjectHandler implements ObjectHandlerInterface */ private $actionGroups = []; + /** + * Instance of ObjectExtensionUtil class + * + * @var ObjectExtensionUtil + */ + private $extendUtil; + /** * Singleton getter for instance of ActionGroupObjectHandler * @@ -55,7 +63,7 @@ public static function getInstance() */ private function __construct() { - // private constructor + $this->extendUtil = new ObjectExtensionUtil(); } /** @@ -66,8 +74,9 @@ private function __construct() */ public function getObject($actionGroupName) { - if (array_key_exists($actionGroupName, $this->getAllObjects())) { - return $this->getAllObjects()[$actionGroupName]; + if (array_key_exists($actionGroupName, $this->actionGroups)) { + $actionGroupObject = $this->actionGroups[$actionGroupName]; + return $this->extendActionGroup($actionGroupObject); } return null; @@ -80,6 +89,9 @@ public function getObject($actionGroupName) */ public function getAllObjects() { + foreach ($this->actionGroups as $actionGroupName => $actionGroup) { + $this->actionGroups[$actionGroupName] = $this->extendActionGroup($actionGroup); + } return $this->actionGroups; } @@ -106,4 +118,18 @@ private function initActionGroups() $actionGroupObjectExtractor->extractActionGroup($actionGroupData); } } + + /** + * This method checks if the action group is extended and creates a new action group object accordingly + * + * @param ActionGroupObject $actionGroupObject + * @return ActionGroupObject + */ + private function extendActionGroup($actionGroupObject) + { + if ($actionGroupObject->getParentName() !== null) { + return $this->extendUtil->extendActionGroup($actionGroupObject); + } + return $actionGroupObject; + } } diff --git a/src/Magento/FunctionalTestingFramework/Test/Handlers/TestObjectHandler.php b/src/Magento/FunctionalTestingFramework/Test/Handlers/TestObjectHandler.php index 721c5df84..025ebb079 100644 --- a/src/Magento/FunctionalTestingFramework/Test/Handlers/TestObjectHandler.php +++ b/src/Magento/FunctionalTestingFramework/Test/Handlers/TestObjectHandler.php @@ -5,11 +5,13 @@ */ namespace Magento\FunctionalTestingFramework\Test\Handlers; +use Magento\FunctionalTestingFramework\Exceptions\TestReferenceException; use Magento\FunctionalTestingFramework\Exceptions\XmlException; use Magento\FunctionalTestingFramework\ObjectManager\ObjectHandlerInterface; use Magento\FunctionalTestingFramework\ObjectManagerFactory; use Magento\FunctionalTestingFramework\Test\Objects\TestObject; use Magento\FunctionalTestingFramework\Test\Parsers\TestDataParser; +use Magento\FunctionalTestingFramework\Test\Util\ObjectExtensionUtil; use Magento\FunctionalTestingFramework\Test\Util\TestObjectExtractor; /** @@ -33,6 +35,13 @@ class TestObjectHandler implements ObjectHandlerInterface */ private $tests = []; + /** + * Instance of ObjectExtensionUtil class + * + * @var ObjectExtensionUtil + */ + private $extendUtil; + /** * Singleton method to return TestObjectHandler. * @@ -54,7 +63,7 @@ public static function getInstance() */ private function __construct() { - // private constructor + $this->extendUtil = new ObjectExtensionUtil(); } /** @@ -62,15 +71,16 @@ private function __construct() * * @param string $testName * @return TestObject + * @throws TestReferenceException */ public function getObject($testName) { if (!array_key_exists($testName, $this->tests)) { - trigger_error("Test ${testName} not defined in xml.", E_USER_ERROR); - return null; + throw new TestReferenceException("Test ${testName} not defined in xml."); } + $testObject = $this->tests[$testName]; - return $this->tests[$testName]; + return $this->extendTest($testObject); } /** @@ -80,6 +90,9 @@ public function getObject($testName) */ public function getAllObjects() { + foreach ($this->tests as $testName => $test) { + $this->tests[$testName] = $this->extendTest($test); + } return $this->tests; } @@ -130,4 +143,18 @@ private function initTestData() $this->tests[$testName] = $testObjectExtractor->extractTestData($testData); } } + + /** + * This method checks if the test is extended and creates a new test object accordingly + * + * @param TestObject $testObject + * @return TestObject + */ + private function extendTest($testObject) + { + if ($testObject->getParentName() !== null) { + return $this->extendUtil->extendTest($testObject); + } + return $testObject; + } } diff --git a/src/Magento/FunctionalTestingFramework/Test/Objects/ActionGroupObject.php b/src/Magento/FunctionalTestingFramework/Test/Objects/ActionGroupObject.php index 7cd73f120..36773af3b 100644 --- a/src/Magento/FunctionalTestingFramework/Test/Objects/ActionGroupObject.php +++ b/src/Magento/FunctionalTestingFramework/Test/Objects/ActionGroupObject.php @@ -7,8 +7,10 @@ namespace Magento\FunctionalTestingFramework\Test\Objects; use Magento\FunctionalTestingFramework\Exceptions\TestReferenceException; +use Magento\FunctionalTestingFramework\Test\Handlers\ActionGroupObjectHandler; use Magento\FunctionalTestingFramework\Test\Util\ActionGroupObjectExtractor; use Magento\FunctionalTestingFramework\Test\Util\ActionMergeUtil; +use Magento\FunctionalTestingFramework\Test\Util\ObjectExtension; /** * Class ActionGroupObject @@ -45,14 +47,22 @@ class ActionGroupObject */ private $arguments; + /** + * String of parent Action Group + * + * @var String + */ + private $parentActionGroup; + /** * ActionGroupObject constructor. * * @param string $name * @param ArgumentObject[] $arguments * @param array $actions + * @param string $parentActionGroup */ - public function __construct($name, $arguments, $actions) + public function __construct($name, $arguments, $actions, $parentActionGroup) { $this->varAttributes = array_merge( ActionObject::SELECTOR_ENABLED_ATTRIBUTES, @@ -62,6 +72,7 @@ public function __construct($name, $arguments, $actions) $this->name = $name; $this->arguments = $arguments; $this->parsedActions = $actions; + $this->parentActionGroup = $parentActionGroup; } /** @@ -362,6 +373,46 @@ public function extractStepKeys() return $originalKeys; } + /** + * Getter for the Action Group Name + * + * @return string + */ + public function getName() + { + return $this->name; + } + + /** + * Getter for the Parent Action Group Name + * + * @return string + */ + public function getParentName() + { + return $this->parentActionGroup; + } + + /** + * Getter for the Action Group Actions + * + * @return ActionObject[] + */ + public function getActions() + { + return $this->parsedActions; + } + + /** + * Getter for the Action Group Arguments + * + * @return array + */ + public function getArguments() + { + return $this->arguments; + } + /** * Searches through ActionGroupObject's arguments and returns first argument wi * @param string $name diff --git a/src/Magento/FunctionalTestingFramework/Test/Objects/TestHookObject.php b/src/Magento/FunctionalTestingFramework/Test/Objects/TestHookObject.php index 2c51b84e1..678df6b84 100644 --- a/src/Magento/FunctionalTestingFramework/Test/Objects/TestHookObject.php +++ b/src/Magento/FunctionalTestingFramework/Test/Objects/TestHookObject.php @@ -65,6 +65,16 @@ public function getType() return $this->type; } + /** + * Getter for hook parent name + * + * @return string + */ + public function getParentName() + { + return $this->parentName; + } + /** * Returns an array of action objects to be executed within the hook. * @@ -76,6 +86,16 @@ public function getActions() return $mergeUtil->resolveActionSteps($this->actions); } + /** + * Returns an array of unresolved actions + * + * @return array + */ + public function getUnresolvedActions() + { + return $this->actions; + } + /** * Returns an array of customData to be interperpreted by the generator. * @return array|null diff --git a/src/Magento/FunctionalTestingFramework/Test/Objects/TestObject.php b/src/Magento/FunctionalTestingFramework/Test/Objects/TestObject.php index 8d8700868..7a316fc3c 100644 --- a/src/Magento/FunctionalTestingFramework/Test/Objects/TestObject.php +++ b/src/Magento/FunctionalTestingFramework/Test/Objects/TestObject.php @@ -50,6 +50,13 @@ class TestObject */ private $filename; + /** + * String of parent test + * + * @var String + */ + private $parentTest; + /** * TestObject constructor. * @@ -58,14 +65,16 @@ class TestObject * @param array $annotations * @param TestHookObject[] $hooks * @param String $filename + * @param String $parentTest */ - public function __construct($name, $parsedSteps, $annotations, $hooks, $filename = null) + public function __construct($name, $parsedSteps, $annotations, $hooks, $filename = null, $parentTest = null) { $this->name = $name; $this->parsedSteps = $parsedSteps; $this->annotations = $annotations; $this->hooks = $hooks; $this->filename = $filename; + $this->parentTest = $parentTest; } /** @@ -88,6 +97,16 @@ public function getFilename() return $this->filename; } + /** + * Getter for the Parent Test Name + * + * @return string + */ + public function getParentName() + { + return $this->parentTest; + } + /** * Getter for the skip_test boolean * @@ -128,7 +147,7 @@ public function getAnnotations() /** * Returns hooks. * - * @return array + * @return TestHookObject[] */ public function getHooks() { @@ -200,6 +219,16 @@ public function getOrderedActions() return $mergeUtil->resolveActionSteps($this->parsedSteps); } + /** + * This method returns currently parsed steps + * + * @return array + */ + public function getUnresolvedSteps() + { + return $this->parsedSteps; + } + /** * Get information about actions and steps in test. * diff --git a/src/Magento/FunctionalTestingFramework/Test/Util/ActionGroupObjectExtractor.php b/src/Magento/FunctionalTestingFramework/Test/Util/ActionGroupObjectExtractor.php index 9fd912da3..fd8093358 100644 --- a/src/Magento/FunctionalTestingFramework/Test/Util/ActionGroupObjectExtractor.php +++ b/src/Magento/FunctionalTestingFramework/Test/Util/ActionGroupObjectExtractor.php @@ -21,6 +21,7 @@ class ActionGroupObjectExtractor extends BaseObjectExtractor const FILENAME = 'filename'; const ACTION_GROUP_INSERT_BEFORE = "insertBefore"; const ACTION_GROUP_INSERT_AFTER = "insertAfter"; + const EXTENDS_ACTION_GROUP = 'extends'; /** * Action Object Extractor for converting actions into objects @@ -48,6 +49,7 @@ public function extractActionGroup($actionGroupData) { $arguments = []; + $actionGroupReference = $actionGroupData[self::EXTENDS_ACTION_GROUP] ?? null; $actionData = $this->stripDescriptorTags( $actionGroupData, self::NODE_NAME, @@ -55,7 +57,8 @@ public function extractActionGroup($actionGroupData) self::NAME, self::FILENAME, self::ACTION_GROUP_INSERT_BEFORE, - self::ACTION_GROUP_INSERT_AFTER + self::ACTION_GROUP_INSERT_AFTER, + self::EXTENDS_ACTION_GROUP ); // TODO filename is now available to the ActionGroupObject, integrate this into debug and error statements @@ -68,7 +71,8 @@ public function extractActionGroup($actionGroupData) return new ActionGroupObject( $actionGroupData[self::NAME], $arguments, - $actions + $actions, + $actionGroupReference ); } diff --git a/src/Magento/FunctionalTestingFramework/Test/Util/ActionObjectExtractor.php b/src/Magento/FunctionalTestingFramework/Test/Util/ActionObjectExtractor.php index 932e5c96e..95c6f1501 100644 --- a/src/Magento/FunctionalTestingFramework/Test/Util/ActionObjectExtractor.php +++ b/src/Magento/FunctionalTestingFramework/Test/Util/ActionObjectExtractor.php @@ -90,7 +90,7 @@ public function extractActions($testActions, $testName = null) $returnVariable = $actionData[ActionGroupObjectHandler::TEST_ACTION_RETURN_VARIABLE]; }*/ - $actions[] = new ActionObject( + $actions[$stepKey] = new ActionObject( $stepKey, $actionData[self::NODE_NAME], $actionAttributes, diff --git a/src/Magento/FunctionalTestingFramework/Test/Util/ObjectExtensionUtil.php b/src/Magento/FunctionalTestingFramework/Test/Util/ObjectExtensionUtil.php new file mode 100644 index 000000000..14a02739d --- /dev/null +++ b/src/Magento/FunctionalTestingFramework/Test/Util/ObjectExtensionUtil.php @@ -0,0 +1,231 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\FunctionalTestingFramework\Test\Util; + +use Magento\FunctionalTestingFramework\Config\MftfApplicationConfig; +use Magento\FunctionalTestingFramework\Exceptions\TestReferenceException; +use Magento\FunctionalTestingFramework\Exceptions\XmlException; +use Magento\FunctionalTestingFramework\Test\Handlers\ActionGroupObjectHandler; +use Magento\FunctionalTestingFramework\Test\Objects\ActionGroupObject; +use Magento\FunctionalTestingFramework\Test\Objects\ActionObject; +use Magento\FunctionalTestingFramework\Test\Objects\TestObject; +use Magento\FunctionalTestingFramework\Test\Objects\TestHookObject; +use Magento\FunctionalTestingFramework\Test\Handlers\TestObjectHandler; + +class ObjectExtensionUtil +{ + /** + * ObjectExtensionUtil constructor. + */ + public function __construct() + { + // empty + } + + /** + * Resolves test references for extending test objects + * + * @param TestObject $testObject + * @return TestObject + * @throws TestReferenceException|XmlException + */ + public function extendTest($testObject) + { + // Check to see if the parent test exists + try { + $parentTest = TestObjectHandler::getInstance()->getObject($testObject->getParentName()); + } catch (TestReferenceException $error) { + if (MftfApplicationConfig::getConfig()->verboseEnabled()) { + echo( + "Parent Test " . + $testObject->getParentName() . + " not defined for Test " . + $testObject->getName() . + ". Skipping Test." . + PHP_EOL); + } + $skippedTest = $this->skipTest($testObject); + return $skippedTest; + } + + // Check to see if the parent test is already an extended test + if ($parentTest->getParentName() !== null) { + throw new XmlException( + "Cannot extend a test that already extends another test. Test: " . $parentTest->getName() + ); + } + if (MftfApplicationConfig::getConfig()->verboseEnabled()) { + echo("Extending Test: " . $parentTest->getName() . " => " . $testObject->getName() . PHP_EOL); + } + + // Get steps for both the parent and the child tests + $referencedTestSteps = $parentTest->getUnresolvedSteps(); + $newSteps = $this->processRemoveActions(array_merge($referencedTestSteps, $testObject->getUnresolvedSteps())); + + $testHooks = $this->resolveExtendedHooks($testObject, $parentTest); + + // Create new Test object to return + $extendedTest = new TestObject( + $testObject->getName(), + $newSteps, + $testObject->getAnnotations(), + $testHooks, + $testObject->getFilename(), + $testObject->getParentName() + ); + return $extendedTest; + } + + /** + * Resolves test references for extending action group objects + * + * @param ActionGroupObject $actionGroupObject + * @return ActionGroupObject + * @throws XmlException + */ + public function extendActionGroup($actionGroupObject) + { + // Check to see if the parent action group exists + $parentActionGroup = ActionGroupObjectHandler::getInstance()->getObject($actionGroupObject->getParentName()); + if ($parentActionGroup == null) { + throw new XmlException( + "Parent Action Group " . + $actionGroupObject->getParentName() . + " not defined for Test " . + $actionGroupObject->getName() . + "." . + PHP_EOL + ); + } + + // Check to see if the parent action group is already an extended action group + if ($parentActionGroup->getParentName() !== null) { + throw new XmlException( + "Cannot extend an action group that already extends another action group. " . + $parentActionGroup->getName() + ); + } + if (MftfApplicationConfig::getConfig()->verboseEnabled()) { + echo("Extending Action Group: " . + $parentActionGroup->getName() . + " => " . + $actionGroupObject->getName() . + PHP_EOL + ); + } + + // Get steps for both the parent and the child action groups + $referencedActions = $parentActionGroup->getActions(); + $newActions = $this->processRemoveActions(array_merge($referencedActions, $actionGroupObject->getActions())); + + $extendedArguments = array_merge( + $actionGroupObject->getArguments(), + $parentActionGroup->getArguments() + ); + + // Create new Action Group object to return + $extendedActionGroup = new ActionGroupObject( + $actionGroupObject->getName(), + $extendedArguments, + $newActions, + $actionGroupObject->getParentName() + ); + return $extendedActionGroup; + } + + /** + * Resolves test references for extending test objects + * + * @param TestObject $testObject + * @param TestObject $parentTestObject + * @return TestHookObject[] $testHooks + */ + private function resolveExtendedHooks($testObject, $parentTestObject) + { + $testHooks = $testObject->getHooks(); + $parentHooks = $parentTestObject->getHooks(); + + // Get the hooks for each Test merge changes from the child hooks to the parent hooks into the child hooks + foreach ($testHooks as $key => $hook) { + if (array_key_exists($key, $parentHooks)) { + $testHookActions = array_merge( + $parentHooks[$key]->getUnresolvedActions(), + $testHooks[$key]->getUnresolvedActions() + ); + $cleanedTestHookActions = $this->processRemoveActions($testHookActions); + + $newTestHook = new TestHookObject( + $parentHooks[$key]->getType(), + $parentHooks[$key]->getParentName(), + $cleanedTestHookActions + ); + $testHooks[$key] = $newTestHook; + } else { + $testHooks[$key] = $hook; + } + } + + // Add parent hooks to child if they did not originally exist on the child + foreach ($parentHooks as $key => $hook) { + if (!array_key_exists($key, $testHooks)) { + $testHooks[$key] = $hook; + } + } + + return $testHooks; + } + + /** + * Resolves test references for removing actions in extended test + * + * @param ActionObject[] $actions + * @return ActionObject[] + * @throws XmlException + */ + private function processRemoveActions($actions) + { + $cleanedActions = []; + + // remove actions merged that are of type 'remove' + foreach ($actions as $actionName => $actionData) { + if ($actionData->getType() != "remove") { + $cleanedActions[$actionName] = $actionData; + } + } + + return $cleanedActions; + } + + /** + * This method returns a skipped form of the Test Object + * + * @param TestObject $testObject + * @return TestObject + */ + public function skipTest($testObject) + { + $annotations = $testObject->getAnnotations(); + + // Add skip to the group array if it doesn't already exist + if (array_key_exists('group', $annotations) && !in_array('skip', $annotations['group'])) { + array_push($annotations['group'], 'skip'); + } elseif (!array_key_exists('group', $annotations)) { + $annotations['group'] = ['skip']; + } + + $skippedTest = new TestObject( + $testObject->getName(), + [], + $annotations, + [], + $testObject->getFilename(), + $testObject->getParentName() + ); + + return $skippedTest; + } +} diff --git a/src/Magento/FunctionalTestingFramework/Test/Util/TestObjectExtractor.php b/src/Magento/FunctionalTestingFramework/Test/Util/TestObjectExtractor.php index cd39753ba..45f13aed6 100644 --- a/src/Magento/FunctionalTestingFramework/Test/Util/TestObjectExtractor.php +++ b/src/Magento/FunctionalTestingFramework/Test/Util/TestObjectExtractor.php @@ -77,6 +77,7 @@ public function extractTestData($testData) $fileNames = explode(",", $filename); $baseFileName = $fileNames[0]; $module = $this->extractModuleName($baseFileName); + $testReference = $testData['extends'] ?? null; $testActions = $this->stripDescriptorTags( $testData, self::NODE_NAME, @@ -87,7 +88,8 @@ public function extractTestData($testData) self::TEST_FAILED_HOOK, self::TEST_INSERT_BEFORE, self::TEST_INSERT_AFTER, - self::TEST_FILENAME + self::TEST_FILENAME, + 'extends' ); if (array_key_exists(self::TEST_ANNOTATIONS, $testData)) { @@ -127,7 +129,8 @@ public function extractTestData($testData) $this->actionObjectExtractor->extractActions($testActions, $testData[self::NAME]), $testAnnotations, $testHooks, - $filename + $filename, + $testReference ); } catch (XmlException $exception) { throw new XmlException($exception->getMessage() . ' in Test ' . $filename); diff --git a/src/Magento/FunctionalTestingFramework/Test/etc/actionGroupSchema.xsd b/src/Magento/FunctionalTestingFramework/Test/etc/actionGroupSchema.xsd index 5df793643..619d21e9f 100644 --- a/src/Magento/FunctionalTestingFramework/Test/etc/actionGroupSchema.xsd +++ b/src/Magento/FunctionalTestingFramework/Test/etc/actionGroupSchema.xsd @@ -35,6 +35,7 @@ <xs:attribute type="xs:string" name="filename"/> <xs:attribute type="xs:string" name="insertBefore"/> <xs:attribute type="xs:string" name="insertAfter"/> + <xs:attribute type="xs:string" name="extends"/> </xs:complexType> <xs:simpleType name="dataTypeEnum" final="restriction"> <xs:restriction base="xs:string"> diff --git a/src/Magento/FunctionalTestingFramework/Test/etc/mergedTestSchema.xsd b/src/Magento/FunctionalTestingFramework/Test/etc/mergedTestSchema.xsd index ecd058581..0f8aae936 100644 --- a/src/Magento/FunctionalTestingFramework/Test/etc/mergedTestSchema.xsd +++ b/src/Magento/FunctionalTestingFramework/Test/etc/mergedTestSchema.xsd @@ -72,6 +72,7 @@ <xs:attribute type="xs:string" name="filename"/> <xs:attribute type="xs:string" name="insertBefore"/> <xs:attribute type="xs:string" name="insertAfter"/> + <xs:attribute type="xs:string" name="extends"/> </xs:complexType> <xs:group name="testTypeTags"> <xs:choice> From 621e9c580bc0f6510a221097ad6eed44c2230737 Mon Sep 17 00:00:00 2001 From: Alex Calandra <calandra.aj@gmail.com> Date: Thu, 17 May 2018 11:46:18 -0500 Subject: [PATCH 039/111] MQE-993: ActionGroups that use createData are not generated correctly (#129) * MQE-993: ActionGroups that use createData are not generated correctly - Added check in getResolvedActionsWithArgs * MQE-993: ActionGroups that use createData are not generated correctly - Abstracted method to private function * MQE-993: ActionGroups that use createData are not generated correctly - Removed broken out line in ActionGroupObject * MQE-993: ActionGroups that use createData are not generated correctly - Removed nested if --- .../Resources/ActionGroupUsingCreateData.txt | 58 +++++++++++++++++++ .../ActionGroup/BasicActionGroup.xml | 7 +++ .../TestModule/Test/ActionGroupTest.xml | 6 ++ .../Tests/ActionGroupGenerationTest.php | 11 ++++ .../Test/Objects/ActionGroupObject.php | 33 ++++++++++- 5 files changed, 113 insertions(+), 2 deletions(-) create mode 100644 dev/tests/verification/Resources/ActionGroupUsingCreateData.txt diff --git a/dev/tests/verification/Resources/ActionGroupUsingCreateData.txt b/dev/tests/verification/Resources/ActionGroupUsingCreateData.txt new file mode 100644 index 000000000..2acf9e297 --- /dev/null +++ b/dev/tests/verification/Resources/ActionGroupUsingCreateData.txt @@ -0,0 +1,58 @@ +<?php +namespace Magento\AcceptanceTest\_default\Backend; + +use Magento\FunctionalTestingFramework\AcceptanceTester; +use Magento\FunctionalTestingFramework\DataGenerator\Handlers\DataObjectHandler; +use Magento\FunctionalTestingFramework\DataGenerator\Persist\DataPersistenceHandler; +use Magento\FunctionalTestingFramework\DataGenerator\Objects\EntityDataObject; +use \Codeception\Util\Locator; +use Yandex\Allure\Adapter\Annotation\Features; +use Yandex\Allure\Adapter\Annotation\Stories; +use Yandex\Allure\Adapter\Annotation\Title; +use Yandex\Allure\Adapter\Annotation\Description; +use Yandex\Allure\Adapter\Annotation\Parameter; +use Yandex\Allure\Adapter\Annotation\Severity; +use Yandex\Allure\Adapter\Model\SeverityLevel; +use Yandex\Allure\Adapter\Annotation\TestCaseId; + +/** + */ +class ActionGroupUsingCreateDataCest +{ + /** + * @var DataPersistenceHandler $createCategoryKey1; + */ + protected $createCategoryKey1; + + /** + * @var DataPersistenceHandler $createConfigProductKey1; + */ + protected $createConfigProductKey1; + + /** + * @param AcceptanceTester $I + * @throws \Exception + */ + public function _before(AcceptanceTester $I) + { + $I->amGoingTo("create entity that has the stepKey: createCategoryKey1"); + $ApiCategory = DataObjectHandler::getInstance()->getObject("ApiCategory"); + $this->createCategoryKey1 = new DataPersistenceHandler($ApiCategory, []); + $this->createCategoryKey1->createEntity(); + $I->amGoingTo("create entity that has the stepKey: createConfigProductKey1"); + $ApiConfigurableProduct = DataObjectHandler::getInstance()->getObject("ApiConfigurableProduct"); + $this->createConfigProductKey1 = new DataPersistenceHandler($ApiConfigurableProduct, [$this->createCategoryKey1]); + $this->createConfigProductKey1->createEntity(); + } + + /** + * @Features({"TestModule"}) + * @Parameter(name = "AcceptanceTester", value="$I") + * @param AcceptanceTester $I + * @return void + * @throws \Exception + */ + public function ActionGroupUsingCreateData(AcceptanceTester $I) + { + } +} diff --git a/dev/tests/verification/TestModule/ActionGroup/BasicActionGroup.xml b/dev/tests/verification/TestModule/ActionGroup/BasicActionGroup.xml index b53aa9db8..732812e60 100644 --- a/dev/tests/verification/TestModule/ActionGroup/BasicActionGroup.xml +++ b/dev/tests/verification/TestModule/ActionGroup/BasicActionGroup.xml @@ -91,4 +91,11 @@ <actionGroup name="extendRemoveTestActionGroup" extends="extendBasicActionGroup"> <remove keyForRemoval="removeMe"/> </actionGroup> + + <actionGroup name="actionGroupWithCreateData"> + <createData entity="ApiCategory" stepKey="createCategory"/> + <createData entity="ApiConfigurableProduct" stepKey="createConfigProduct"> + <requiredEntity createDataKey="createCategory"/> + </createData> + </actionGroup> </actionGroups> diff --git a/dev/tests/verification/TestModule/Test/ActionGroupTest.xml b/dev/tests/verification/TestModule/Test/ActionGroupTest.xml index b784ce8f3..c28c6d9f3 100644 --- a/dev/tests/verification/TestModule/Test/ActionGroupTest.xml +++ b/dev/tests/verification/TestModule/Test/ActionGroupTest.xml @@ -137,4 +137,10 @@ <test name="ExtendedRemoveActionGroup"> <actionGroup ref="extendRemoveTestActionGroup" stepKey="actionGroup"/> </test> + + <test name="ActionGroupUsingCreateData"> + <before> + <actionGroup ref="actionGroupWithCreateData" stepKey="Key1"/> + </before> + </test> </tests> diff --git a/dev/tests/verification/Tests/ActionGroupGenerationTest.php b/dev/tests/verification/Tests/ActionGroupGenerationTest.php index f7ce2b3fc..3985ab2eb 100644 --- a/dev/tests/verification/Tests/ActionGroupGenerationTest.php +++ b/dev/tests/verification/Tests/ActionGroupGenerationTest.php @@ -162,4 +162,15 @@ public function testExtendedRemoveActionGroup() { $this->generateAndCompareTest('ExtendedRemoveActionGroup'); } + + /** + * Test generation of a test referencing an action group that uses stepKey references within the action group + * + * @throws \Exception + * @throws \Magento\FunctionalTestingFramework\Exceptions\TestReferenceException + */ + public function testActionGroupWithCreateData() + { + $this->generateAndCompareTest('ActionGroupUsingCreateData'); + } } diff --git a/src/Magento/FunctionalTestingFramework/Test/Objects/ActionGroupObject.php b/src/Magento/FunctionalTestingFramework/Test/Objects/ActionGroupObject.php index 36773af3b..a75fc3145 100644 --- a/src/Magento/FunctionalTestingFramework/Test/Objects/ActionGroupObject.php +++ b/src/Magento/FunctionalTestingFramework/Test/Objects/ActionGroupObject.php @@ -136,15 +136,21 @@ private function resolveArguments($arguments) private function getResolvedActionsWithArgs($arguments, $actionReferenceKey) { $resolvedActions = []; + $replacementStepKeys = []; foreach ($this->parsedActions as $action) { + $replacementStepKeys[$action->getStepKey()] = $action->getStepKey() . ucfirst($actionReferenceKey); $varAttributes = array_intersect($this->varAttributes, array_keys($action->getCustomActionAttributes())); + + // replace createDataKey attributes inside the action group + $resolvedActionAttributes = $this->replaceCreateDataKeys($action, $replacementStepKeys); + $newActionAttributes = []; if (!empty($varAttributes)) { $newActionAttributes = $this->resolveAttributesWithArguments( $arguments, - $action->getCustomActionAttributes() + $resolvedActionAttributes ); } @@ -159,7 +165,7 @@ private function getResolvedActionsWithArgs($arguments, $actionReferenceKey) $resolvedActions[$action->getStepKey() . ucfirst($actionReferenceKey)] = new ActionObject( $action->getStepKey() . ucfirst($actionReferenceKey), $action->getType(), - array_replace_recursive($action->getCustomActionAttributes(), $newActionAttributes), + array_replace_recursive($resolvedActionAttributes, $newActionAttributes), $action->getLinkedAction() == null ? null : $action->getLinkedAction() . ucfirst($actionReferenceKey), $orderOffset, [self::ACTION_GROUP_ORIGIN_NAME => $this->name, @@ -432,4 +438,27 @@ function ($e) use ($name) { } return null; } + + /** + * Replaces references to step keys used earlier in an action group + * + * @param ActionObject $action + * @param array $replacementStepKeys + * @return ActionObject[] + */ + private function replaceCreateDataKeys($action, $replacementStepKeys) + { + $resolvedActionAttributes = []; + + foreach ($action->getCustomActionAttributes() as $actionAttribute => $actionAttributeDetails) { + if (is_array($actionAttributeDetails) && array_key_exists('createDataKey', $actionAttributeDetails)) { + $actionAttributeDetails['createDataKey'] = + $replacementStepKeys[$actionAttributeDetails['createDataKey']] ?? + $actionAttributeDetails['createDataKey']; + } + $resolvedActionAttributes[$actionAttribute] = $actionAttributeDetails; + } + + return $resolvedActionAttributes; + } } From 8d8d856a6517508a4ded9490837329d5099f570b Mon Sep 17 00:00:00 2001 From: Ian Meron <imeron@magento.com> Date: Tue, 15 May 2018 16:16:04 -0500 Subject: [PATCH 040/111] MQE-1011: Keyword Comment In User Input For Action Group Substitutes Incorrectly - fixed issue with stepKey matching argument value replacement --- .../ActionGroupContainsStepKeyInArgText.txt | 42 +++++++++++++++++++ .../ActionGroup/BasicActionGroup.xml | 7 ++++ .../TestModule/Test/ActionGroupTest.xml | 11 +++++ .../Tests/ActionGroupGenerationTest.php | 11 +++++ .../Util/TestGenerator.php | 13 +++++- 5 files changed, 82 insertions(+), 2 deletions(-) create mode 100644 dev/tests/verification/Resources/ActionGroupContainsStepKeyInArgText.txt diff --git a/dev/tests/verification/Resources/ActionGroupContainsStepKeyInArgText.txt b/dev/tests/verification/Resources/ActionGroupContainsStepKeyInArgText.txt new file mode 100644 index 000000000..c41170b5c --- /dev/null +++ b/dev/tests/verification/Resources/ActionGroupContainsStepKeyInArgText.txt @@ -0,0 +1,42 @@ +<?php +namespace Magento\AcceptanceTest\_default\Backend; + +use Magento\FunctionalTestingFramework\AcceptanceTester; +use Magento\FunctionalTestingFramework\DataGenerator\Handlers\DataObjectHandler; +use Magento\FunctionalTestingFramework\DataGenerator\Persist\DataPersistenceHandler; +use Magento\FunctionalTestingFramework\DataGenerator\Objects\EntityDataObject; +use \Codeception\Util\Locator; +use Yandex\Allure\Adapter\Annotation\Features; +use Yandex\Allure\Adapter\Annotation\Stories; +use Yandex\Allure\Adapter\Annotation\Title; +use Yandex\Allure\Adapter\Annotation\Description; +use Yandex\Allure\Adapter\Annotation\Parameter; +use Yandex\Allure\Adapter\Annotation\Severity; +use Yandex\Allure\Adapter\Model\SeverityLevel; +use Yandex\Allure\Adapter\Annotation\TestCaseId; + +/** + */ +class ActionGroupContainsStepKeyInArgTextCest +{ + /** + * @param AcceptanceTester $I + * @throws \Exception + */ + public function _before(AcceptanceTester $I) + { + $I->see("arg1", ".selector"); + } + + /** + * @Features({"TestModule"}) + * @Parameter(name = "AcceptanceTester", value="$I") + * @param AcceptanceTester $I + * @return void + * @throws \Exception + */ + public function ActionGroupContainsStepKeyInArgText(AcceptanceTester $I) + { + $I->see("arg1", ".selector"); + } +} diff --git a/dev/tests/verification/TestModule/ActionGroup/BasicActionGroup.xml b/dev/tests/verification/TestModule/ActionGroup/BasicActionGroup.xml index 732812e60..30c0c03c7 100644 --- a/dev/tests/verification/TestModule/ActionGroup/BasicActionGroup.xml +++ b/dev/tests/verification/TestModule/ActionGroup/BasicActionGroup.xml @@ -98,4 +98,11 @@ <requiredEntity createDataKey="createCategory"/> </createData> </actionGroup> + + <actionGroup name="actionGroupContainsStepKeyInArgValue"> + <arguments> + <argument name="sameStepKeyAsArg" type="string" defaultValue="stringLiteral"/> + </arguments> + <see selector=".selector" userInput="{{sameStepKeyAsArg}}" stepKey="arg1" /> + </actionGroup> </actionGroups> diff --git a/dev/tests/verification/TestModule/Test/ActionGroupTest.xml b/dev/tests/verification/TestModule/Test/ActionGroupTest.xml index c28c6d9f3..ccace672b 100644 --- a/dev/tests/verification/TestModule/Test/ActionGroupTest.xml +++ b/dev/tests/verification/TestModule/Test/ActionGroupTest.xml @@ -143,4 +143,15 @@ <actionGroup ref="actionGroupWithCreateData" stepKey="Key1"/> </before> </test> + + <test name="ActionGroupContainsStepKeyInArgText"> + <before> + <actionGroup ref="actionGroupContainsStepKeyInArgValue" stepKey="actionGroup"> + <argument name="sameStepKeyAsArg" value="arg1"/> + </actionGroup> + </before> + <actionGroup ref="actionGroupContainsStepKeyInArgValue" stepKey="actionGroup"> + <argument name="sameStepKeyAsArg" value="arg1"/> + </actionGroup> + </test> </tests> diff --git a/dev/tests/verification/Tests/ActionGroupGenerationTest.php b/dev/tests/verification/Tests/ActionGroupGenerationTest.php index 3985ab2eb..d7b57baed 100644 --- a/dev/tests/verification/Tests/ActionGroupGenerationTest.php +++ b/dev/tests/verification/Tests/ActionGroupGenerationTest.php @@ -173,4 +173,15 @@ public function testActionGroupWithCreateData() { $this->generateAndCompareTest('ActionGroupUsingCreateData'); } + + /** + * Test an action group with an arg containing stepKey text + * + * @throws \Exception + * @throws \Magento\FunctionalTestingFramework\Exceptions\TestReferenceException + */ + public function testActionGroupWithArgContainingStepKey() + { + $this->generateAndCompareTest('ActionGroupContainsStepKeyInArgText'); + } } diff --git a/src/Magento/FunctionalTestingFramework/Util/TestGenerator.php b/src/Magento/FunctionalTestingFramework/Util/TestGenerator.php index 211f010c0..789aa04f8 100644 --- a/src/Magento/FunctionalTestingFramework/Util/TestGenerator.php +++ b/src/Magento/FunctionalTestingFramework/Util/TestGenerator.php @@ -1385,9 +1385,18 @@ private function resolveStepKeyReferences($input, $actionGroupOrigin) $testInvocationKey = ucfirst($actionGroupOrigin[ActionGroupObject::ACTION_GROUP_ORIGIN_TEST_REF]); foreach ($stepKeys as $stepKey) { - if (strpos($output, $stepKey)) { - $output = str_replace($stepKey, $stepKey . $testInvocationKey, $output); + // MQE-1011 + $stepKeyVarRef = "$" . $stepKey; + $classVarRef = "\$this->$stepKey"; + + if (strpos($output, $stepKeyVarRef) !== false) { + $output = str_replace($stepKeyVarRef, $stepKeyVarRef . $testInvocationKey, $output); + } + + if (strpos($output, $classVarRef) !== false) { + $output = str_replace($classVarRef, $classVarRef . $testInvocationKey, $output); } + } return $output; } From 1af30e0a255951308a202e0afed709cdbb44471d Mon Sep 17 00:00:00 2001 From: Ian Meron <imeron@magento.com> Date: Fri, 25 May 2018 10:26:14 -0500 Subject: [PATCH 041/111] MQE-987: Decouple MFTF from Magento - add new mftf console commands - include new bootstrap from mftf autoloading - update tests and pathing to be windows compatible --- RoboFile.php | 214 ------------------ bin/mftf | 16 +- bootstrap.php | 8 - composer.json | 1 + dev/tests/_bootstrap.php | 10 +- dev/tests/functional/MFTF/_bootstrap.php | 7 - dev/tests/functional/_bootstrap.php | 31 ++- .../MFTF}/DevDocs/Page/MFTFDocPage.xml | 0 .../MFTF}/DevDocs/Section/ContentSection.xml | 0 .../MFTF}/DevDocs/Test/DevDocsTest.xml | 0 .../Suite/SuiteGeneratorTest.php | 12 +- .../Tests/SuiteGenerationTest.php | 55 ++--- .env.example => etc/config/.env.example | 0 .../config/codeception.dist.yml | 10 +- .../config/functional.suite.dist.yml | 7 +- .../Config/FileResolver/Root.php | 4 +- .../Console/BuildProjectCommand.php | 81 +++++-- .../Console/CleanProjectCommand.php | 92 ++++++++ .../Console/GenerateSuiteCommand.php | 56 +++++ .../Console/GenerateTestsCommand.php | 140 ++++++++++++ .../Console/RunTestCommand.php | 73 ++++++ .../Console/RunTestGroupCommand.php | 104 +++++++++ .../Console/SetupEnvCommand.php | 8 +- .../Suite/SuiteGenerator.php | 5 +- .../Util/Env/EnvProcessor.php | 49 +++- .../Util/Manifest/BaseTestManifest.php | 3 +- .../Util/ModuleResolver.php | 1 + .../FunctionalTestingFramework/_bootstrap.php | 63 ++++++ 28 files changed, 715 insertions(+), 335 deletions(-) delete mode 100644 RoboFile.php delete mode 100644 bootstrap.php delete mode 100755 dev/tests/functional/MFTF/_bootstrap.php rename dev/tests/functional/{MFTF/FunctionalTest => tests/MFTF}/DevDocs/Page/MFTFDocPage.xml (100%) rename dev/tests/functional/{MFTF/FunctionalTest => tests/MFTF}/DevDocs/Section/ContentSection.xml (100%) rename dev/tests/functional/{MFTF/FunctionalTest => tests/MFTF}/DevDocs/Test/DevDocsTest.xml (100%) rename .env.example => etc/config/.env.example (100%) rename codeception.dist.yml => etc/config/codeception.dist.yml (72%) rename dev/tests/functional/MFTF.suite.dist.yml => etc/config/functional.suite.dist.yml (84%) create mode 100644 src/Magento/FunctionalTestingFramework/Console/CleanProjectCommand.php create mode 100644 src/Magento/FunctionalTestingFramework/Console/GenerateSuiteCommand.php create mode 100644 src/Magento/FunctionalTestingFramework/Console/GenerateTestsCommand.php create mode 100644 src/Magento/FunctionalTestingFramework/Console/RunTestCommand.php create mode 100644 src/Magento/FunctionalTestingFramework/Console/RunTestGroupCommand.php create mode 100644 src/Magento/FunctionalTestingFramework/_bootstrap.php diff --git a/RoboFile.php b/RoboFile.php deleted file mode 100644 index c0af4c72c..000000000 --- a/RoboFile.php +++ /dev/null @@ -1,214 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ - -/** This is project's console commands configuration for Robo task runner. - * - * @codingStandardsIgnoreStart - * @see http://robo.li/ - */ -class RoboFile extends \Robo\Tasks -{ - use Robo\Task\Base\loadShortcuts; - - /** - * Duplicate the Example configuration files used to customize the Project for customization. - * - * @return void - */ - function cloneFiles() - { - $this->_exec('cp -vn .env.example .env'); - $this->_exec('cp -vf codeception.dist.yml codeception.yml'); - $this->_exec('cp -vf dev' . DIRECTORY_SEPARATOR . 'tests'. DIRECTORY_SEPARATOR . 'functional' . DIRECTORY_SEPARATOR .'MFTF.suite.dist.yml dev' . DIRECTORY_SEPARATOR . 'tests'. DIRECTORY_SEPARATOR . 'functional' . DIRECTORY_SEPARATOR .'MFTF.suite.yml'); - } - - /** - * Duplicate the Example configuration files for the Project. - * Build the Codeception project. - * - * @return void - */ - function buildProject() - { - $this->writeln("<error>This command will be removed in MFTF v3.0.0. Please use bin/mftf build:project instead.</error>\n"); - $this->cloneFiles(); - $this->_exec('vendor'. DIRECTORY_SEPARATOR .'bin'. DIRECTORY_SEPARATOR .'codecept build'); - } - - /** - * Generate all Tests in PHP. - * - * @param array $tests - * @param array $opts - * @return void - */ - function generateTests(array $tests, $opts = ['config' => null, 'force' => true, 'nodes' => null, 'debug' => false]) - { - require 'dev' . DIRECTORY_SEPARATOR . 'tests'. DIRECTORY_SEPARATOR . 'functional' . DIRECTORY_SEPARATOR . '_bootstrap.php'; - $GLOBALS['GENERATE_TESTS'] = true; - if (!$this->isProjectBuilt()) { - $this->say("<info>Please run bin/mftf build:project and configure your environment (.env) first.</info>"); - exit(\Robo\Result::EXITCODE_ERROR); - } - $testsObjects = []; - foreach ($tests as $test) { - $testsObjects[] = Magento\FunctionalTestingFramework\Test\Handlers\TestObjectHandler::getInstance()->getObject($test); - } - if ($opts['force']) { - $GLOBALS['FORCE_PHP_GENERATE'] = true; - } - $testsReferencedInSuites = \Magento\FunctionalTestingFramework\Suite\SuiteGenerator::getInstance()->generateAllSuites($opts['config']); - \Magento\FunctionalTestingFramework\Util\TestGenerator::getInstance(null, $testsObjects, $opts['debug'])->createAllTestFiles($opts['config'], $opts['nodes'], $testsReferencedInSuites); - $this->say("<comment>Generate Tests Command Run</comment>"); - } - - /** - * Check if MFTF has been properly configured - * @return bool - */ - private function isProjectBuilt() - { - $actorFile = __DIR__ . DIRECTORY_SEPARATOR . 'src' . DIRECTORY_SEPARATOR . 'Magento' . DIRECTORY_SEPARATOR . 'FunctionalTestingFramework' . DIRECTORY_SEPARATOR . '_generated' . DIRECTORY_SEPARATOR . 'AcceptanceTesterActions.php'; - - $login = !empty(getenv('MAGENTO_ADMIN_USERNAME')); - $password = !empty(getenv('MAGENTO_ADMIN_PASSWORD')); - $baseUrl = !empty(getenv('MAGENTO_BASE_URL')); - $backendName = !empty(getenv('MAGENTO_BACKEND_NAME')); - $test = (file_exists($actorFile) && $login && $password && $baseUrl && $backendName); - return $test; - } - - /** - * Generate a suite based on name(s) passed in as args. - * - * @param array $args - * @throws Exception - * @return void - */ - function generateSuite(array $args) - { - if (empty($args)) { - throw new Exception("Please provide suite name(s) after generate:suite command"); - } - - require 'dev' . DIRECTORY_SEPARATOR . 'tests'. DIRECTORY_SEPARATOR . 'functional' . DIRECTORY_SEPARATOR . '_bootstrap.php'; - $sg = \Magento\FunctionalTestingFramework\Suite\SuiteGenerator::getInstance(); - - foreach ($args as $arg) { - $sg->generateSuite($arg); - } - } - - /** - * Run all MFTF tests. - * - * @return void - */ - function mftf() - { - $this->_exec('.' . DIRECTORY_SEPARATOR . 'vendor' . DIRECTORY_SEPARATOR . 'bin' . DIRECTORY_SEPARATOR . 'codecept run MFTF --skip-group skip'); - } - - /** - * Run all Tests with the specified @group tag, excluding @group 'skip'. - * - * @param string $args - * @return void - */ - function group($args = '') - { - $this->taskExec('.' . DIRECTORY_SEPARATOR . 'vendor' . DIRECTORY_SEPARATOR . 'bin' . DIRECTORY_SEPARATOR . 'codecept run --verbose --steps --skip-group skip --group')->args($args)->run(); - } - - /** - * Run all Functional tests located under the Directory Path provided. - * - * @param string $args - * @return void - */ - function folder($args = '') - { - $this->taskExec('.' . DIRECTORY_SEPARATOR . 'vendor' . DIRECTORY_SEPARATOR . 'bin' . DIRECTORY_SEPARATOR . 'codecept run ')->args($args)->run(); - } - - /** - * Run all Tests marked with the @group tag 'example'. - * - * @return void - */ - function example() - { - $this->_exec('.' . DIRECTORY_SEPARATOR . 'vendor' . DIRECTORY_SEPARATOR . 'bin' . DIRECTORY_SEPARATOR . 'codecept run --group example --skip-group skip'); - } - - /** - * Generate the HTML for the Allure report based on the Test XML output - Allure v1.4.X - * - * @return \Robo\Result - */ - function allure1Generate() - { - return $this->_exec('allure generate tests'. DIRECTORY_SEPARATOR .'_output'. DIRECTORY_SEPARATOR .'allure-results'. DIRECTORY_SEPARATOR .' -o tests'. DIRECTORY_SEPARATOR .'_output'. DIRECTORY_SEPARATOR .'allure-report'. DIRECTORY_SEPARATOR .''); - } - - /** - * Generate the HTML for the Allure report based on the Test XML output - Allure v2.3.X - * - * @return \Robo\Result - */ - function allure2Generate() - { - return $this->_exec('allure generate tests'. DIRECTORY_SEPARATOR .'_output'. DIRECTORY_SEPARATOR .'allure-results'. DIRECTORY_SEPARATOR .' --output tests'. DIRECTORY_SEPARATOR .'_output'. DIRECTORY_SEPARATOR .'allure-report'. DIRECTORY_SEPARATOR .' --clean'); - } - - /** - * Open the HTML Allure report - Allure v1.4.X - * - * @return void - */ - function allure1Open() - { - $this->_exec('allure report open --report-dir tests'. DIRECTORY_SEPARATOR .'_output'. DIRECTORY_SEPARATOR .'allure-report'. DIRECTORY_SEPARATOR .''); - } - - /** - * Open the HTML Allure report - Allure v2.3.X - * - * @return void - */ - function allure2Open() - { - $this->_exec('allure open --port 0 tests'. DIRECTORY_SEPARATOR .'_output'. DIRECTORY_SEPARATOR .'allure-report'. DIRECTORY_SEPARATOR .''); - } - - /** - * Generate and open the HTML Allure report - Allure v1.4.X - * - * @return void - */ - function allure1Report() - { - $result1 = $this->allure1Generate(); - - if ($result1->wasSuccessful()) { - $this->allure1Open(); - } - } - - /** - * Generate and open the HTML Allure report - Allure v2.3.X - * - * @return void - */ - function allure2Report() - { - $result1 = $this->allure2Generate(); - - if ($result1->wasSuccessful()) { - $this->allure2Open(); - } - } -} diff --git a/bin/mftf b/bin/mftf index 623e3bc61..b8c1cce80 100755 --- a/bin/mftf +++ b/bin/mftf @@ -11,13 +11,27 @@ if (PHP_SAPI !== 'cli') { exit(1); } +$autoload_path = realpath(__DIR__ . '/../../../autoload.php'); +$test_bootstrap_path = realpath(__DIR__ . '/../dev/tests/functional/_bootstrap.php'); + +if (file_exists($autoload_path)) { + require_once $autoload_path; +} else { + require_once $test_bootstrap_path; +} + + try { - require_once __DIR__ . '/../bootstrap.php'; $application = new Symfony\Component\Console\Application(); $application->setName('Magento Functional Testing Framework CLI'); $application->setVersion('1.0.0'); $application->add(new Magento\FunctionalTestingFramework\Console\SetupEnvCommand()); + $application->add(new Magento\FunctionalTestingFramework\Console\CleanProjectCommand()); $application->add(new Magento\FunctionalTestingFramework\Console\BuildProjectCommand()); + $application->add(new Magento\FunctionalTestingFramework\Console\GenerateSuiteCommand()); + $application->add(new Magento\FunctionalTestingFramework\Console\GenerateTestsCommand()); + $application->add(new Magento\FunctionalTestingFramework\Console\RunTestGroupCommand()); + $application->add(new Magento\FunctionalTestingFramework\Console\RunTestCommand()); $application->run(); } catch (\Exception $e) { while ($e) { diff --git a/bootstrap.php b/bootstrap.php deleted file mode 100644 index 7a708e8a8..000000000 --- a/bootstrap.php +++ /dev/null @@ -1,8 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ - -require_once 'vendor/autoload.php'; -define('BP', __DIR__); diff --git a/composer.json b/composer.json index d404a4fe8..0b88b59e3 100755 --- a/composer.json +++ b/composer.json @@ -34,6 +34,7 @@ "symfony/stopwatch": "~3.4.6" }, "autoload": { + "files": ["src/Magento/FunctionalTestingFramework/_bootstrap.php"], "psr-4": { "Magento\\FunctionalTestingFramework\\": "src/Magento/FunctionalTestingFramework", "MFTF\\": "dev/tests/functional/MFTF" diff --git a/dev/tests/_bootstrap.php b/dev/tests/_bootstrap.php index a93459fae..0baf41c56 100644 --- a/dev/tests/_bootstrap.php +++ b/dev/tests/_bootstrap.php @@ -6,14 +6,18 @@ error_reporting(~E_USER_NOTICE); define('PROJECT_ROOT', dirname(dirname(__DIR__))); -require_once PROJECT_ROOT . '/vendor/autoload.php'; -require_once 'util/MftfTestCase.php'; + +$vendorAutoloadPath = realpath(PROJECT_ROOT . '/vendor/autoload.php'); +$mftfTestCasePath = realpath(PROJECT_ROOT . '/dev/tests/util/MftfTestCase.php'); + +require_once $vendorAutoloadPath; +require_once $mftfTestCasePath; // Set up AspectMock $kernel = \AspectMock\Kernel::getInstance(); $kernel->init([ 'debug' => true, - 'includePaths' => [PROJECT_ROOT . '/src'], + 'includePaths' => [PROJECT_ROOT . DIRECTORY_SEPARATOR . 'src'], 'cacheDir' => PROJECT_ROOT . DIRECTORY_SEPARATOR . 'dev' . diff --git a/dev/tests/functional/MFTF/_bootstrap.php b/dev/tests/functional/MFTF/_bootstrap.php deleted file mode 100755 index 6a524e867..000000000 --- a/dev/tests/functional/MFTF/_bootstrap.php +++ /dev/null @@ -1,7 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ - -require_once dirname(__DIR__) . DIRECTORY_SEPARATOR . '_bootstrap.php'; \ No newline at end of file diff --git a/dev/tests/functional/_bootstrap.php b/dev/tests/functional/_bootstrap.php index e51658f23..3a5e73310 100755 --- a/dev/tests/functional/_bootstrap.php +++ b/dev/tests/functional/_bootstrap.php @@ -5,22 +5,9 @@ */ define('PROJECT_ROOT', dirname(dirname(dirname(__DIR__)))); -require_once PROJECT_ROOT . '/vendor/autoload.php'; -$RELATIVE_FW_PATH = PROJECT_ROOT; +require_once realpath(PROJECT_ROOT . '/vendor/autoload.php'); //Load constants from .env file -if (file_exists(PROJECT_ROOT . '/.env')) { - $env = new \Dotenv\Loader(PROJECT_ROOT . '/.env'); - $env->load(); - - if (array_key_exists('TESTS_MODULE_PATH', $_ENV) xor array_key_exists('TESTS_BP', $_ENV)) { - throw new Exception('You must define both parameters TESTS_BP and TESTS_MODULE_PATH or neither parameter'); - } - - foreach ($_ENV as $key => $var) { - defined($key) || define($key, $var); - } -} defined('FW_BP') || define('FW_BP', PROJECT_ROOT); // add the debug flag here @@ -29,7 +16,17 @@ xdebug_disable(); } -$RELATIVE_TESTS_MODULE_PATH = '/MFTF/FunctionalTest'; +$RELATIVE_TESTS_MODULE_PATH = '/tests/functional/tests/MFTF'; -defined('TESTS_BP') || define('TESTS_BP', __DIR__); -defined('TESTS_MODULE_PATH') || define('TESTS_MODULE_PATH', TESTS_BP . $RELATIVE_TESTS_MODULE_PATH); +defined('MAGENTO_BP') || define('MAGENTO_BP', PROJECT_ROOT); +defined('TESTS_BP') || define('TESTS_BP', dirname(dirname(__DIR__))); +defined('TESTS_MODULE_PATH') || define('TESTS_MODULE_PATH', realpath(TESTS_BP . $RELATIVE_TESTS_MODULE_PATH)); + +if (file_exists(TESTS_BP . DIRECTORY_SEPARATOR . '.env')) { + $env = new \Dotenv\Loader(TESTS_BP . DIRECTORY_SEPARATOR . '.env'); + $env->load(); + + foreach ($_ENV as $key => $var) { + defined($key) || define($key, $var); + } +} diff --git a/dev/tests/functional/MFTF/FunctionalTest/DevDocs/Page/MFTFDocPage.xml b/dev/tests/functional/tests/MFTF/DevDocs/Page/MFTFDocPage.xml similarity index 100% rename from dev/tests/functional/MFTF/FunctionalTest/DevDocs/Page/MFTFDocPage.xml rename to dev/tests/functional/tests/MFTF/DevDocs/Page/MFTFDocPage.xml diff --git a/dev/tests/functional/MFTF/FunctionalTest/DevDocs/Section/ContentSection.xml b/dev/tests/functional/tests/MFTF/DevDocs/Section/ContentSection.xml similarity index 100% rename from dev/tests/functional/MFTF/FunctionalTest/DevDocs/Section/ContentSection.xml rename to dev/tests/functional/tests/MFTF/DevDocs/Section/ContentSection.xml diff --git a/dev/tests/functional/MFTF/FunctionalTest/DevDocs/Test/DevDocsTest.xml b/dev/tests/functional/tests/MFTF/DevDocs/Test/DevDocsTest.xml similarity index 100% rename from dev/tests/functional/MFTF/FunctionalTest/DevDocs/Test/DevDocsTest.xml rename to dev/tests/functional/tests/MFTF/DevDocs/Test/DevDocsTest.xml diff --git a/dev/tests/unit/Magento/FunctionalTestFramework/Suite/SuiteGeneratorTest.php b/dev/tests/unit/Magento/FunctionalTestFramework/Suite/SuiteGeneratorTest.php index 8eb486144..d6790f00e 100644 --- a/dev/tests/unit/Magento/FunctionalTestFramework/Suite/SuiteGeneratorTest.php +++ b/dev/tests/unit/Magento/FunctionalTestFramework/Suite/SuiteGeneratorTest.php @@ -23,6 +23,17 @@ class SuiteGeneratorTest extends TestCase { + /** + * Setup entry append and clear for Suite Generator + */ + public static function setUpBeforeClass() + { + AspectMock::double(SuiteGenerator::class, [ + 'clearPreviousSessionConfigEntries' => null, + 'appendEntriesToConfig' => null + ]); + } + /** * Tests generating a single suite given a set of parsed test data * @throws \Exception @@ -169,6 +180,5 @@ private function setMockTestAndSuiteParserOutput($testData, $suiteData) $property = new \ReflectionProperty(SuiteGenerator::class, 'groupClassGenerator'); $property->setAccessible(true); $property->setValue($instance, $instance); - } } diff --git a/dev/tests/verification/Tests/SuiteGenerationTest.php b/dev/tests/verification/Tests/SuiteGenerationTest.php index 5f2d3a6d2..4024329f1 100644 --- a/dev/tests/verification/Tests/SuiteGenerationTest.php +++ b/dev/tests/verification/Tests/SuiteGenerationTest.php @@ -8,15 +8,17 @@ use Magento\FunctionalTestingFramework\Suite\SuiteGenerator; use Magento\FunctionalTestingFramework\Util\Filesystem\DirSetupUtil; +use Magento\FunctionalTestingFramework\Util\Manifest\DefaultTestManifest; use Magento\FunctionalTestingFramework\Util\Manifest\ParallelTestManifest; use Magento\FunctionalTestingFramework\Util\Manifest\TestManifestFactory; +use PHPUnit\Util\Filesystem; use Symfony\Component\Yaml\Yaml; use tests\util\MftfTestCase; class SuiteGenerationTest extends MftfTestCase { const RESOURCES_DIR = TESTS_BP . DIRECTORY_SEPARATOR . 'verification' . DIRECTORY_SEPARATOR . 'Resources'; - const CONFIG_YML_FILE = FW_BP . DIRECTORY_SEPARATOR . SuiteGenerator::YAML_CODECEPTION_CONFIG_FILENAME; + const CONFIG_YML_FILE = TESTS_BP . DIRECTORY_SEPARATOR . SuiteGenerator::YAML_CODECEPTION_CONFIG_FILENAME; const GENERATE_RESULT_DIR = TESTS_BP . DIRECTORY_SEPARATOR . "verification" . @@ -24,13 +26,6 @@ class SuiteGenerationTest extends MftfTestCase "_generated" . DIRECTORY_SEPARATOR; - /** - * Flag to track existence of config.yml file - * - * @var bool - */ - private static $YML_EXISTS_FLAG = false; - /** * Array which stores state of any existing config.yml groups * @@ -43,18 +38,20 @@ class SuiteGenerationTest extends MftfTestCase */ public static function setUpBeforeClass() { - if (file_exists(self::CONFIG_YML_FILE)) { - self::$YML_EXISTS_FLAG = true; - return; - } - // destroy _generated if it exists if (file_exists(self::GENERATE_RESULT_DIR)) { DirSetupUtil::rmdirRecursive(self::GENERATE_RESULT_DIR); } + } - $configYml = fopen(self::CONFIG_YML_FILE, "w"); - fclose($configYml); + public function setUp() + { + // copy config yml file to test dir + $fileSystem = new \Symfony\Component\Filesystem\Filesystem(); + $fileSystem->copy( + realpath(FW_BP . '/etc/config/codeception.dist.yml'), + self::CONFIG_YML_FILE + ); } /** @@ -200,7 +197,6 @@ public function testSuiteGenerationHooks() self::RESOURCES_PATH . DIRECTORY_SEPARATOR . $groupName . ".txt", $groupFile ); - } /** @@ -219,7 +215,7 @@ public function testSuiteGenerationSingleRun() ]; //createParallelManifest - /** @var ParallelTestManifest $parallelManifest */ + /** @var DefaultTestManifest $parallelManifest */ $singleRunManifest = TestManifestFactory::makeManifest("singleRun", ["functionalSuite2" => []]); // Generate the Suite @@ -246,7 +242,7 @@ public function testSuiteGenerationSingleRun() $this->assertTrue(in_array($expectedFile, $dirContents)); } - $expectedManifest = "dev/tests/verification/_generated/default/" . PHP_EOL . "-g functionalSuite2" . PHP_EOL; + $expectedManifest = "verification/_generated/default/" . PHP_EOL . "-g functionalSuite2" . PHP_EOL; $this->assertEquals($expectedManifest, file_get_contents(self::getManifestFilePath())); } @@ -257,26 +253,13 @@ public function testSuiteGenerationSingleRun() */ public function tearDown() { - // restore config if we see there was an original codeception.yml file - if (self::$YML_EXISTS_FLAG) { - $yml = Yaml::parse(file_get_contents(self::CONFIG_YML_FILE)); - foreach (self::$TEST_GROUPS as $testGroup) { - unset($yml['groups'][$testGroup]); - } - - file_put_contents(self::CONFIG_YML_FILE, Yaml::dump($yml, 10)); - } DirSetupUtil::rmdirRecursive(self::GENERATE_RESULT_DIR); - } - /** - * Remove yml if created during tests and did not exist before - */ - public static function tearDownAfterClass() - { - if (!self::$YML_EXISTS_FLAG) { - unlink(self::CONFIG_YML_FILE); - } + // delete config yml file from test dir + $fileSystem = new \Symfony\Component\Filesystem\Filesystem(); + $fileSystem->remove( + self::CONFIG_YML_FILE + ); } /** diff --git a/.env.example b/etc/config/.env.example similarity index 100% rename from .env.example rename to etc/config/.env.example diff --git a/codeception.dist.yml b/etc/config/codeception.dist.yml similarity index 72% rename from codeception.dist.yml rename to etc/config/codeception.dist.yml index 25093c681..273ede0b0 100755 --- a/codeception.dist.yml +++ b/etc/config/codeception.dist.yml @@ -2,19 +2,19 @@ # See COPYING.txt for license details. actor: Tester paths: - tests: dev/tests/functional - log: dev/tests/functional/_output - data: dev/tests/functional/_data + tests: tests + log: tests/_output + data: tests/_data support: src/Magento/FunctionalTestingFramework envs: etc/_envs settings: - bootstrap: _bootstrap.php colors: true memory_limit: 1024M extensions: enabled: - Codeception\Extension\RunFailed - - Yandex\Allure\Adapter\AllureAdapter + - Magento\FunctionalTestingFramework\Extension\TestContextExtension + - Magento\FunctionalTestingFramework\Allure\Adapter\MagentoAllureAdapter config: Yandex\Allure\Adapter\AllureAdapter: deletePreviousResults: true diff --git a/dev/tests/functional/MFTF.suite.dist.yml b/etc/config/functional.suite.dist.yml similarity index 84% rename from dev/tests/functional/MFTF.suite.dist.yml rename to etc/config/functional.suite.dist.yml index a54620385..bfd093e41 100644 --- a/dev/tests/functional/MFTF.suite.dist.yml +++ b/etc/config/functional.suite.dist.yml @@ -14,14 +14,15 @@ modules: - \Magento\FunctionalTestingFramework\Module\MagentoWebDriver - \Magento\FunctionalTestingFramework\Helper\Acceptance - \Magento\FunctionalTestingFramework\Helper\MagentoFakerData + - \Magento\FunctionalTestingFramework\Module\MagentoSequence - \Magento\FunctionalTestingFramework\Module\MagentoAssert - Asserts config: \Magento\FunctionalTestingFramework\Module\MagentoWebDriver: url: "%MAGENTO_BASE_URL%" backend_name: "%MAGENTO_BACKEND_NAME%" - browser: '%BROWSER%' - window_size: maximize + browser: 'chrome' + window_size: 1280x1024 username: "%MAGENTO_ADMIN_USERNAME%" password: "%MAGENTO_ADMIN_PASSWORD%" pageload_timeout: 30 @@ -31,5 +32,5 @@ modules: path: %SELENIUM_PATH% capabilities: chromeOptions: - args: ["--start-maximized", "--disable-extensions", "--enable-automation"] + args: ["--incognito", "--disable-extensions", "--enable-automation"] diff --git a/src/Magento/FunctionalTestingFramework/Config/FileResolver/Root.php b/src/Magento/FunctionalTestingFramework/Config/FileResolver/Root.php index 93e74a82c..3b0940b28 100644 --- a/src/Magento/FunctionalTestingFramework/Config/FileResolver/Root.php +++ b/src/Magento/FunctionalTestingFramework/Config/FileResolver/Root.php @@ -11,7 +11,7 @@ class Root extends Module { - const ROOT_SUITE_DIR = "_suite"; + const ROOT_SUITE_DIR = "tests/_suite"; /** * Retrieve the list of configuration files with given name that relate to specified scope at the root level as well @@ -25,7 +25,7 @@ public function get($filename, $scope) { // first pick up the root level test suite dir $paths = glob( - dirname(TESTS_BP) . DIRECTORY_SEPARATOR . self::ROOT_SUITE_DIR + TESTS_BP . DIRECTORY_SEPARATOR . self::ROOT_SUITE_DIR . DIRECTORY_SEPARATOR . $filename ); diff --git a/src/Magento/FunctionalTestingFramework/Console/BuildProjectCommand.php b/src/Magento/FunctionalTestingFramework/Console/BuildProjectCommand.php index 6e29ac071..ea5c5795f 100644 --- a/src/Magento/FunctionalTestingFramework/Console/BuildProjectCommand.php +++ b/src/Magento/FunctionalTestingFramework/Console/BuildProjectCommand.php @@ -16,9 +16,12 @@ use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Process\Process; use Magento\FunctionalTestingFramework\Util\Env\EnvProcessor; +use Symfony\Component\Yaml\Yaml; class BuildProjectCommand extends Command { + const DEFAULT_YAML_INLINE_DEPTH = 10; + /** * Env processor manages .env files. * @@ -33,9 +36,9 @@ class BuildProjectCommand extends Command */ protected function configure() { - $this->setName('build:project'); - $this->setDescription('Generate configuration files for the project. Build the Codeception project.'); - $this->envProcessor = new EnvProcessor(BP . DIRECTORY_SEPARATOR . '.env'); + $this->setName('build:project') + ->setDescription('Generate configuration files for the project. Build the Codeception project.'); + $this->envProcessor = new EnvProcessor(TESTS_BP . DIRECTORY_SEPARATOR . '.env'); $env = $this->envProcessor->getEnv(); foreach ($env as $key => $value) { $this->addOption($key, null, InputOption::VALUE_REQUIRED, '', $value); @@ -49,22 +52,12 @@ protected function configure() * @param OutputInterface $output * @return void * @throws \Symfony\Component\Console\Exception\LogicException + * + * @SuppressWarnings(PHPMD.UnusedLocalVariable) */ protected function execute(InputInterface $input, OutputInterface $output) { - $fileSystem = new Filesystem(); - $fileSystem->copy( - BP . DIRECTORY_SEPARATOR . 'codeception.dist.yml', - BP . DIRECTORY_SEPARATOR . 'codeception.yml' - ); - $output->writeln("codeception.yml configuration successfully applied.\n"); - $fileSystem->copy( - BP . DIRECTORY_SEPARATOR . 'dev' . DIRECTORY_SEPARATOR . 'tests' . DIRECTORY_SEPARATOR . - 'functional' . DIRECTORY_SEPARATOR . 'MFTF.suite.dist.yml', - BP . DIRECTORY_SEPARATOR . 'dev' . DIRECTORY_SEPARATOR . 'tests' . DIRECTORY_SEPARATOR . - 'functional' . DIRECTORY_SEPARATOR . 'MFTF.suite.yml' - ); - $output->writeln("MFTF.suite.yml configuration successfully applied.\n"); + $this->generateConfigFiles($output); $setupEnvCommand = new SetupEnvCommand(); $commandInput = []; @@ -78,12 +71,58 @@ protected function execute(InputInterface $input, OutputInterface $output) $commandInput = new ArrayInput($commandInput); $setupEnvCommand->run($commandInput, $output); - $process = new Process('vendor/bin/codecept build'); - $process->run(); - if ($process->isSuccessful()) { - $output->writeln("Codeception build run successfully.\n"); + + // TODO can we just import the codecept symfony command? + $codeceptBuildCommand = realpath(PROJECT_ROOT . '/vendor/bin/codecept') . ' build'; + $process = new Process($codeceptBuildCommand); + $process->setWorkingDirectory(TESTS_BP); + $process->run( + function ($type, $buffer) use ($output) { + if ($output->isVerbose()) { + $output->write($buffer); + } + } + ); + } + + /** + * Generates needed codeception configuration files to the TEST_BP directory + * + * @param OutputInterface $output + * @return void + */ + private function generateConfigFiles(OutputInterface $output) + { + $fileSystem = new Filesystem(); + //Find travel path from codeception.yml to FW_BP + $relativePath = $fileSystem->makePathRelative(FW_BP, TESTS_BP); + + if (!$fileSystem->exists(TESTS_BP . DIRECTORY_SEPARATOR . 'codeception.yml')) { + // read in the codeception.yml file + $configDistYml = Yaml::parse(file_get_contents(realpath(FW_BP . "/etc/config/codeception.dist.yml"))); + $configDistYml['paths']['support'] = $relativePath . 'src/Magento/FunctionalTestingFramework'; + $configDistYml['paths']['envs'] = $relativePath . 'etc/_envs'; + $configYmlText = Yaml::dump($configDistYml, self::DEFAULT_YAML_INLINE_DEPTH); + + // dump output to new codeception.yml file + file_put_contents(TESTS_BP . DIRECTORY_SEPARATOR . 'codeception.yml', $configYmlText); + $output->writeln("codeception.yml configuration successfully applied."); } - $output->writeln('<info>The project built successfully.</info>'); + if ($output->isVerbose()) { + $output->writeln("codeception.yml applied to " . TESTS_BP . DIRECTORY_SEPARATOR . 'codeception.yml'); + } + + // copy the functional suite yml, this will only copy if there are differences between the template the destination + $fileSystem->copy( + realpath(FW_BP . '/etc/config/functional.suite.dist.yml'), + TESTS_BP . DIRECTORY_SEPARATOR . 'tests' . DIRECTORY_SEPARATOR . 'functional.suite.yml' + ); + $output->writeln('functional.suite.yml configuration successfully applied.'); + + if ($output->isVerbose()) { + $output->writeln("functional.suite.yml applied to " . + TESTS_BP . DIRECTORY_SEPARATOR . 'tests' . DIRECTORY_SEPARATOR . 'functional.suite.yml'); + } } } diff --git a/src/Magento/FunctionalTestingFramework/Console/CleanProjectCommand.php b/src/Magento/FunctionalTestingFramework/Console/CleanProjectCommand.php new file mode 100644 index 000000000..c2d388898 --- /dev/null +++ b/src/Magento/FunctionalTestingFramework/Console/CleanProjectCommand.php @@ -0,0 +1,92 @@ +<?php +// @codingStandardsIgnoreFile +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types = 1); + +namespace Magento\FunctionalTestingFramework\Console; + +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Filesystem\Filesystem; +use Symfony\Component\Finder\Finder; +use Symfony\Component\Finder\SplFileInfo; + +class CleanProjectCommand extends Command +{ + const CONFIGURATION_FILES = [ + TESTS_BP . DIRECTORY_SEPARATOR . 'codeception.yml', // codeception.yml file for top level config + TESTS_BP . DIRECTORY_SEPARATOR . 'tests' . DIRECTORY_SEPARATOR . 'functional.suite.yml', // functional.suite.yml for test execution config + FW_BP . '/src/Magento/FunctionalTestingFramework/_generated', // Acceptance Tester Actions generated by codeception + FW_BP . '/src/Magento/FunctionalTestingFramework/AcceptanceTester.php' // AcceptanceTester Class generated by codeception + ]; + + const GENERATED_FILES = [ + TESTS_MODULE_PATH . '/_generated' + ]; + + /** + * Configures the current command. + * + * @return void + */ + protected function configure() + { + $this->setName('reset') + ->setDescription('This command will clean any configuration files from the environment (not including .env), as well as any generated artifacts.') + ->addOption('hard', null, InputOption::VALUE_NONE, "parameter to force reset of configuration files."); + } + + /** + * Executes the current command. + * + * @param InputInterface $input + * @param OutputInterface $output + * @return void + * @throws \Symfony\Component\Console\Exception\LogicException + */ + protected function execute(InputInterface $input, OutputInterface $output) + { + $isHardReset = $input->getOption('hard'); + $fileSystem = new Filesystem(); + $finder = new Finder(); + $finder->files()->name('*.php')->in(realpath(FW_BP . '/src/Magento/FunctionalTestingFramework/Group/')); + $filesForRemoval = []; + + // include config files if user specifies a hard reset for deletion + if ($isHardReset) { + $filesForRemoval = array_merge($filesForRemoval, self::CONFIGURATION_FILES); + } + + // include the files mftf generates during test execution in TESTS_BP for deletion + $filesForRemoval = array_merge($filesForRemoval, self::GENERATED_FILES); + + if ($output->isVerbose()) { + $output->writeln('Deleting Files:'); + } + + // delete any suite based group files + foreach ($finder->files() as $file) { + if ($output->isVerbose()) { + $output->writeln($file->getRealPath()); + } + + $fileSystem->remove($file); + } + + // delete files specified for removal + foreach ($filesForRemoval as $fileForRemoval) { + if ($fileSystem->exists($fileForRemoval) && $output->isVerbose()) { + $output->writeln($fileForRemoval); + } + + $fileSystem->remove($fileForRemoval); + } + + $output->writeln('mftf files removed from filesystem.'); + } +} diff --git a/src/Magento/FunctionalTestingFramework/Console/GenerateSuiteCommand.php b/src/Magento/FunctionalTestingFramework/Console/GenerateSuiteCommand.php new file mode 100644 index 000000000..d94e5fa2e --- /dev/null +++ b/src/Magento/FunctionalTestingFramework/Console/GenerateSuiteCommand.php @@ -0,0 +1,56 @@ +<?php +// @codingStandardsIgnoreFile +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types = 1); + +namespace Magento\FunctionalTestingFramework\Console; + +use Magento\FunctionalTestingFramework\Suite\SuiteGenerator; +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; + +class GenerateSuiteCommand extends Command +{ + /** + * Configures the current command. + * + * @return void + */ + protected function configure() + { + $this->setName('generate:suite') + ->setDescription('This command generates a single suite based on declaration in xml') + ->addArgument( + 'suites', + InputArgument::IS_ARRAY | InputArgument::REQUIRED, + 'argument which indicates suite names for generation (separated by space)' + ); + } + + /** + * Executes the current command. + * + * @param InputInterface $input + * @param OutputInterface $output + * @return void + * @throws \Symfony\Component\Console\Exception\LogicException + */ + protected function execute(InputInterface $input, OutputInterface $output) + { + $suites = $input->getArgument('suites'); + + foreach ($suites as $suite) { + SuiteGenerator::getInstance()->generateSuite($suite); + if ($output->isVerbose()) { + $output->writeLn("suite $suite generated"); + } + } + + $output->writeLn("Suites Generated"); + } +} diff --git a/src/Magento/FunctionalTestingFramework/Console/GenerateTestsCommand.php b/src/Magento/FunctionalTestingFramework/Console/GenerateTestsCommand.php new file mode 100644 index 000000000..a8d34f1c9 --- /dev/null +++ b/src/Magento/FunctionalTestingFramework/Console/GenerateTestsCommand.php @@ -0,0 +1,140 @@ +<?php +// @codingStandardsIgnoreFile +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types = 1); + +namespace Magento\FunctionalTestingFramework\Console; + +use Magento\FunctionalTestingFramework\Config\MftfApplicationConfig; +use Magento\FunctionalTestingFramework\Exceptions\TestFrameworkException; +use Magento\FunctionalTestingFramework\Suite\SuiteGenerator; +use Magento\FunctionalTestingFramework\Test\Handlers\TestObjectHandler; +use Magento\FunctionalTestingFramework\Util\Manifest\ParallelTestManifest; +use Magento\FunctionalTestingFramework\Util\Manifest\TestManifestFactory; +use Magento\FunctionalTestingFramework\Util\TestGenerator; +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Output\OutputInterface; + +class GenerateTestsCommand extends Command +{ + /** + * Configures the current command. + * + * @return void + */ + protected function configure() + { + $this->setName('generate:tests') + ->setDescription('This command generates all test files and suites based on xml declarations') + ->addArgument('name', InputArgument::OPTIONAL | InputArgument::IS_ARRAY, 'name(s) of specific tests to generate') + ->addOption("config", 'c', InputOption::VALUE_REQUIRED, 'default, singleRun, or parallel', 'default') + ->addOption("force", 'f',InputOption::VALUE_NONE, 'force generation of tests regardless of Magento Instance Configuration') + ->addOption('lines', 'l', InputOption::VALUE_REQUIRED, 'Used in combination with a parallel configuration, determines desired group size', 500) + ->addOption('tests', 't', InputOption::VALUE_REQUIRED, 'A parameter accepting a JSON string used to determine the test configuration'); + } + + /** + * Executes the current command. + * + * @param InputInterface $input + * @param OutputInterface $output + * @return void + * @throws \Symfony\Component\Console\Exception\LogicException + */ + protected function execute(InputInterface $input, OutputInterface $output) + { + $tests = $input->getArgument('name'); + $config = $input->getOption('config'); + $json = $input->getOption('tests'); + $force = $input->getOption('force'); + $lines = $input->getOption('lines'); + $verbose = $output->isVerbose(); + + if ($json !== null && !json_decode($json)) { + // stop execution if we have failed to properly parse any json passed in by the user + throw new TestFrameworkException("JSON could not be parsed: " . json_last_error_msg()); + } + + $testConfiguration = $this->createTestConfiguration($json, $tests, $force, $verbose); + + // create our manifest file here + $testManifest = TestManifestFactory::makeManifest($config, $testConfiguration['suites']); + TestGenerator::getInstance(null, $testConfiguration['tests'])->createAllTestFiles($testManifest); + + if ($config == 'parallel') { + /** @var ParallelTestManifest $testManifest */ + $testManifest->createTestGroups($lines); + } + + SuiteGenerator::getInstance()->generateAllSuites($testManifest); + $testManifest->generate(); + + print "Generate Tests Command Run" . PHP_EOL; + } + + /** + * Function which builds up a configuration including test and suites for consumption of Magento generation methods. + * + * @param string $json + * @param array $tests + * @param bool $force + * @param bool $verbose + * @return array + */ + private function createTestConfiguration($json, array $tests, bool $force, bool $verbose) + { + // set our application configuration so we can references the user options in our framework + MftfApplicationConfig::create( + $force, + MftfApplicationConfig::GENERATION_PHASE, + $verbose + ); + + $testConfiguration = []; + $testConfiguration['tests'] = $tests; + $testConfiguration['suites'] = []; + + $testConfiguration = $this->parseTestsConfigJson($json, $testConfiguration); + + // if we have references to specific tests, we resolve the test objects and pass them to the config + if (!empty($testConfiguration['tests'])) { + $testObjects = []; + + foreach ($testConfiguration['tests'] as $test) { + $testObjects[$test] = TestObjectHandler::getInstance()->getObject($test); + } + + $testConfiguration['tests'] = $testObjects; + } + + return $testConfiguration; + } + + /** + * Function which takes a json string of potential custom configuration and parses/validates the resulting json + * passed in by the user. The result is a testConfiguration array. + * + * @param string $json + * @param array $testConfiguration + * @throws TestFrameworkException + * @return array + */ + private function parseTestsConfigJson($json, array $testConfiguration) { + if ($json === null) { + return $testConfiguration; + } + + $jsonTestConfiguration = []; + $testConfigArray = json_decode($json, true); + + $jsonTestConfiguration['tests'] = $testConfigArray['tests'] ?? null;; + $jsonTestConfiguration['suites'] = $testConfigArray['suites'] ?? null; + return $jsonTestConfiguration; + } +} diff --git a/src/Magento/FunctionalTestingFramework/Console/RunTestCommand.php b/src/Magento/FunctionalTestingFramework/Console/RunTestCommand.php new file mode 100644 index 000000000..6041d0be5 --- /dev/null +++ b/src/Magento/FunctionalTestingFramework/Console/RunTestCommand.php @@ -0,0 +1,73 @@ +<?php +// @codingStandardsIgnoreFile +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types = 1); + +namespace Magento\FunctionalTestingFramework\Console; + +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\ArrayInput; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Process\Process; + +class RunTestCommand extends Command +{ + /** + * Configures the current command. + * + * @return void + */ + protected function configure() + { + $this->setName("run:test") + ->setDescription("generation and execution of test(s) defined in xml") + ->addArgument('name', InputArgument::REQUIRED | InputArgument::IS_ARRAY, "name of tests to generate and execute") + ->addOption('skip-generate', 'k', InputOption::VALUE_NONE, "skip generation and execute existing test"); + } + + /** + * Executes the current command. + * + * @param InputInterface $input + * @param OutputInterface $output + * @return void + * @throws \Symfony\Component\Console\Exception\LogicException + * + * @SuppressWarnings(PHPMD.UnusedLocalVariable) + */ + protected function execute(InputInterface $input, OutputInterface $output) + { + $tests = $input->getArgument('name'); + $skipGeneration = $input->getOption('skip-generate') ?? false; + + if (!$skipGeneration) { + $command = $this->getApplication()->find('generate:tests'); + $args = [ + '--tests' => json_encode([ + 'tests' => $tests, + 'suites' => null + ]) + ]; + + $command->run(new ArrayInput($args), $output); + } + + // we only generate relevant tests here so we can execute "all tests" + $codeceptionCommand = realpath(PROJECT_ROOT . '/vendor/bin/codecept') . " run functional --verbose --steps"; + + $process = new Process($codeceptionCommand); + $process->setWorkingDirectory(TESTS_BP); + $process->run( + function ($type, $buffer) use ($output) { + $output->write($buffer); + } + ); + } + +} diff --git a/src/Magento/FunctionalTestingFramework/Console/RunTestGroupCommand.php b/src/Magento/FunctionalTestingFramework/Console/RunTestGroupCommand.php new file mode 100644 index 000000000..c0e5d99b7 --- /dev/null +++ b/src/Magento/FunctionalTestingFramework/Console/RunTestGroupCommand.php @@ -0,0 +1,104 @@ +<?php +// @codingStandardsIgnoreFile +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types = 1); + +namespace Magento\FunctionalTestingFramework\Console; + +use Magento\FunctionalTestingFramework\Suite\Handlers\SuiteObjectHandler; +use Magento\FunctionalTestingFramework\Test\Handlers\TestObjectHandler; +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\ArrayInput; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Process\Process; + +class RunTestGroupCommand extends Command +{ + /** + * Configures the current command. + * + * @return void + */ + protected function configure() + { + $this->setName('run:group') + ->setDescription('Execute a set of tests referenced via group annotations') + ->addOption('skip-generate', 'k', InputOption::VALUE_NONE, "only execute a group of tests without generating from source xml") + ->addArgument( + 'groups', + InputArgument::IS_ARRAY | InputArgument::REQUIRED, + 'group names to be executed via codeception' + ); + } + + /** + * Executes the current command. + * + * @param InputInterface $input + * @param OutputInterface $output + * @return void + * @throws \Symfony\Component\Console\Exception\LogicException + * + * @SuppressWarnings(PHPMD.UnusedLocalVariable) + */ + protected function execute(InputInterface $input, OutputInterface $output) + { + $skipGeneration = $input->getOption('skip-generate') ?? false; + $groups = $input->getArgument('groups'); + + if (!$skipGeneration) { + $testConfiguration = $this->getGroupAndSuiteConfiguration($groups); + $command = $this->getApplication()->find('generate:tests'); + $args = ['--tests' => $testConfiguration]; + + $command->run(new ArrayInput($args), $output); + } + + $codeceptionCommand = realpath(PROJECT_ROOT . '/vendor/bin/codecept') . ' run functional --verbose --steps'; + + foreach ($groups as $group) { + $codeceptionCommand .= " -g {$group}"; + } + + $process = new Process($codeceptionCommand); + $process->setWorkingDirectory(TESTS_BP); + $process->run( + function ($type, $buffer) use ($output) { + $output->write($buffer); + } + ); + } + + /** + * Returns a json string to be used as an argument for generation of a group or suite + * + * @param array $groups + * @return string + */ + private function getGroupAndSuiteConfiguration(array $groups) + { + $testConfiguration['tests'] = []; + $testConfiguration['suites'] = null; + $availableSuites = SuiteObjectHandler::getInstance()->getAllObjects(); + + foreach ($groups as $group) { + if (array_key_exists($group, $availableSuites)) { + $testConfiguration['suites'][$group] = []; + } + + $testConfiguration['tests'] = array_merge( + $testConfiguration['tests'], + array_keys(TestObjectHandler::getInstance()->getTestsByGroup($group)) + ); + } + + $testConfigurationJson = json_encode($testConfiguration); + return $testConfigurationJson; + } +} diff --git a/src/Magento/FunctionalTestingFramework/Console/SetupEnvCommand.php b/src/Magento/FunctionalTestingFramework/Console/SetupEnvCommand.php index e0ef6053a..5b57c43d7 100644 --- a/src/Magento/FunctionalTestingFramework/Console/SetupEnvCommand.php +++ b/src/Magento/FunctionalTestingFramework/Console/SetupEnvCommand.php @@ -31,9 +31,9 @@ class SetupEnvCommand extends Command */ protected function configure() { - $this->setName('setup:env'); - $this->setDescription("Generate .env file."); - $this->envProcessor = new EnvProcessor(BP . DIRECTORY_SEPARATOR . '.env'); + $this->setName('setup:env') + ->setDescription("Generate .env file."); + $this->envProcessor = new EnvProcessor(TESTS_BP . DIRECTORY_SEPARATOR . '.env'); $env = $this->envProcessor->getEnv(); foreach ($env as $key => $value) { $this->addOption($key, null, InputOption::VALUE_REQUIRED, '', $value); @@ -59,6 +59,6 @@ protected function execute(InputInterface $input, OutputInterface $output) $userEnv[$key] = $input->getOption($key); } $this->envProcessor->putEnvFile($userEnv); - $output->writeln(".env configuration successfully applied.\n"); + $output->writeln(".env configuration successfully applied."); } } diff --git a/src/Magento/FunctionalTestingFramework/Suite/SuiteGenerator.php b/src/Magento/FunctionalTestingFramework/Suite/SuiteGenerator.php index 7ae362591..c7b31610a 100644 --- a/src/Magento/FunctionalTestingFramework/Suite/SuiteGenerator.php +++ b/src/Magento/FunctionalTestingFramework/Suite/SuiteGenerator.php @@ -241,7 +241,8 @@ private function generateGroupFile($suiteName, $tests, $originalSuiteName) */ private function appendEntriesToConfig($suiteName, $suitePath, $groupNamespace) { - $relativeSuitePath = substr($suitePath, strlen(dirname(dirname(TESTS_BP))) + 1); + $relativeSuitePath = substr($suitePath, strlen(TESTS_BP)); + $relativeSuitePath = ltrim($relativeSuitePath, DIRECTORY_SEPARATOR); $ymlArray = self::getYamlFileContents(); if (!array_key_exists(self::YAML_GROUPS_TAG, $ymlArray)) { @@ -344,6 +345,6 @@ private static function getYamlFileContents() */ private static function getYamlConfigFilePath() { - return dirname(dirname(TESTS_BP)) . DIRECTORY_SEPARATOR; + return TESTS_BP . DIRECTORY_SEPARATOR; } } diff --git a/src/Magento/FunctionalTestingFramework/Util/Env/EnvProcessor.php b/src/Magento/FunctionalTestingFramework/Util/Env/EnvProcessor.php index 7129ff091..53871a098 100644 --- a/src/Magento/FunctionalTestingFramework/Util/Env/EnvProcessor.php +++ b/src/Magento/FunctionalTestingFramework/Util/Env/EnvProcessor.php @@ -36,6 +36,13 @@ class EnvProcessor */ private $env = []; + /** + * Boolean indicating existence of env file + * + * @var bool + */ + private $envExists; + /** * EnvProcessor constructor. * @param string $envFile @@ -44,33 +51,50 @@ public function __construct( string $envFile = '' ) { $this->envFile = $envFile; - $this->envExampleFile = $envFile . '.example'; + $this->envExists = file_exists($envFile); + $this->envExampleFile = realpath(FW_BP . "/etc/config/.env.example"); } /** - * Serves for parsing '.env.example' file into associative array. + * Serves for parsing '.env' file into associative array. * * @return array */ - public function parseEnvFile(): array + private function parseEnvFile(): array { - $envLines = file( + $envExampleFile = file( $this->envExampleFile, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES ); - $env = []; - foreach ($envLines as $line) { + + $envContents = []; + if ($this->envExists) { + $envFile = file( + $this->envFile, + FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES + ); + + $envContents = $this->parseEnvFileLines($envFile); + } + + return array_diff_key($this->parseEnvFileLines($envExampleFile), $envContents); + } + + private function parseEnvFileLines(array $file): array + { + $fileArray = []; + foreach ($file as $line) { // do not use commented out lines if (strpos($line, '#') !== 0) { list($key, $value) = explode('=', $line); - $env[$key] = $value; + $fileArray[$key] = $value; } } - return $env; + return $fileArray; } /** - * Serves for putting array with environment variables into .env file. + * Serves for putting array with environment variables into .env file or appending new variables we introduce * * @param array $config * @return void @@ -81,7 +105,12 @@ public function putEnvFile(array $config = []) foreach ($config as $key => $value) { $envData .= $key . '=' . $value . PHP_EOL; } - file_put_contents($this->envFile, $envData); + + if ($this->envExists) { + file_put_contents($this->envFile, $envData, FILE_APPEND); + } else { + file_put_contents($this->envFile, $envData); + } } /** diff --git a/src/Magento/FunctionalTestingFramework/Util/Manifest/BaseTestManifest.php b/src/Magento/FunctionalTestingFramework/Util/Manifest/BaseTestManifest.php index 3c3eb6de9..e5ce1ee50 100644 --- a/src/Magento/FunctionalTestingFramework/Util/Manifest/BaseTestManifest.php +++ b/src/Magento/FunctionalTestingFramework/Util/Manifest/BaseTestManifest.php @@ -43,7 +43,8 @@ abstract class BaseTestManifest public function __construct($path, $runConfig, $suiteConfiguration) { $this->runTypeConfig = $runConfig; - $this->relativeDirPath = substr($path, strlen(dirname(dirname(TESTS_BP))) + 1); + $relativeDirPath = substr($path, strlen(TESTS_BP)); + $this->relativeDirPath = ltrim($relativeDirPath, DIRECTORY_SEPARATOR); $this->suiteConfiguration = $suiteConfiguration; } diff --git a/src/Magento/FunctionalTestingFramework/Util/ModuleResolver.php b/src/Magento/FunctionalTestingFramework/Util/ModuleResolver.php index 43b472b4d..275f8fc57 100644 --- a/src/Magento/FunctionalTestingFramework/Util/ModuleResolver.php +++ b/src/Magento/FunctionalTestingFramework/Util/ModuleResolver.php @@ -229,6 +229,7 @@ private function aggregateTestModulePaths() // Define the Module paths from default TESTS_MODULE_PATH $modulePath = defined('TESTS_MODULE_PATH') ? TESTS_MODULE_PATH : TESTS_BP; + $modulePath = rtrim($modulePath, DIRECTORY_SEPARATOR); // Define the Module paths from vendor modules $vendorCodePath = PROJECT_ROOT diff --git a/src/Magento/FunctionalTestingFramework/_bootstrap.php b/src/Magento/FunctionalTestingFramework/_bootstrap.php new file mode 100644 index 000000000..3dd871adc --- /dev/null +++ b/src/Magento/FunctionalTestingFramework/_bootstrap.php @@ -0,0 +1,63 @@ +<?php +// @codingStandardsIgnoreFile +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +// define framework basepath for schema pathing +defined('FW_BP') || define('FW_BP', realpath(__DIR__ . '/../../../')); + +// get the root path of the project (we will always be installed under vendor) +$projectRootPath = substr(FW_BP, 0, strpos(FW_BP, DIRECTORY_SEPARATOR . 'vendor' . DIRECTORY_SEPARATOR)); + +if (empty($projectRootPath)) { + // Currently we do not support global execution, so leave this script before pathing is set improperly + return; +} + +// set Magento_BP as Root_Project Path +define('PROJECT_ROOT', $projectRootPath); +defined('MAGENTO_BP') || define('MAGENTO_BP', realpath($projectRootPath)); + +// load .env (if it exists) +$envFilepath = realpath(MAGENTO_BP . '/dev/tests/acceptance/'); +if (file_exists($envFilepath . DIRECTORY_SEPARATOR . '.env')) { + $env = new \Dotenv\Loader($envFilepath . DIRECTORY_SEPARATOR . '.env'); + $env->load(); + + if (array_key_exists('TESTS_MODULE_PATH', $_ENV) xor array_key_exists('TESTS_BP', $_ENV)) { + throw new Exception( + 'You must define both parameters TESTS_BP and TESTS_MODULE_PATH or neither parameter' + ); + } + + foreach ($_ENV as $key => $var) { + defined($key) || define($key, $var); + } + + defined('MAGENTO_CLI_COMMAND_PATH') || define( + 'MAGENTO_CLI_COMMAND_PATH', + 'dev/tests/acceptance/utils/command.php' + ); + $env->setEnvironmentVariable('MAGENTO_CLI_COMMAND_PATH', MAGENTO_CLI_COMMAND_PATH); + + defined('MAGENTO_CLI_COMMAND_PARAMETER') || define('MAGENTO_CLI_COMMAND_PARAMETER', 'command'); + $env->setEnvironmentVariable('MAGENTO_CLI_COMMAND_PARAMETER', MAGENTO_CLI_COMMAND_PARAMETER); +} + +// TODO REMOVE THIS CODE ONCE WE HAVE STOPPED SUPPORTING dev/tests/acceptance PATH +// define TEST_PATH and TEST_MODULE_PATH +defined('TESTS_BP') || define('TESTS_BP', realpath(MAGENTO_BP . DIRECTORY_SEPARATOR . 'dev/tests/acceptance/')); + +$RELATIVE_TESTS_MODULE_PATH = '/tests/functional/Magento/FunctionalTest'; +defined('TESTS_MODULE_PATH') || define( + 'TESTS_MODULE_PATH', + realpath(TESTS_BP . $RELATIVE_TESTS_MODULE_PATH) +); + +// add the debug flag here +$debugMode = $_ENV['MFTF_DEBUG'] ?? false; +if (!(bool)$debugMode && extension_loaded('xdebug')) { + xdebug_disable(); +} From 57ad4423483d25897d876a7e503cec3669f8fd1f Mon Sep 17 00:00:00 2001 From: Alex Calandra <calandra.aj@gmail.com> Date: Thu, 31 May 2018 08:48:44 -0500 Subject: [PATCH 042/111] MQE-943: Inconsistency in *Page.xml files declaration for "module" attribute. (#135) * MQE-943: Inconsistency in *Page.xml files declaration for "module" attribute. - Moved Module Extractor method - Extended Page Filesystem and Dom - Added warning for incorrectly named page modules * MQE-943: Inconsistency in *Page.xml files declaration for "module" attribute. - Moved duplicated code to a single location * MQE-943: Inconsistency in *Page.xml files declaration for "module" attribute. - Added Tests for Extractor Class - Updated samplepage.xml * MQE-943: Inconsistency in *Page.xml files declaration for "module" attribute. - Added correct references for filename and exceptionCollector to the merge signature - Fixed issue with Module Resolver on Windows --- .../Util/ModulePathExtractorTest.php | 73 ++++++++++++++++ .../TestModule/Page/SamplePage.xml | 14 ++-- etc/di.xml | 7 +- .../FunctionalTestingFramework/Config/Dom.php | 6 +- .../Reader/MftfFilesystem.php} | 4 +- .../Page/Config/Dom.php | 84 +++++++++++++++++++ .../Test/Util/TestObjectExtractor.php | 32 +++---- .../Util/ModulePathExtractor.php | 51 +++++++++++ .../Util/ModuleResolver.php | 6 +- 9 files changed, 238 insertions(+), 39 deletions(-) create mode 100644 dev/tests/unit/Magento/FunctionalTestFramework/Util/ModulePathExtractorTest.php rename src/Magento/FunctionalTestingFramework/{Test/Config/Reader/Filesystem.php => Config/Reader/MftfFilesystem.php} (94%) create mode 100644 src/Magento/FunctionalTestingFramework/Page/Config/Dom.php create mode 100644 src/Magento/FunctionalTestingFramework/Util/ModulePathExtractor.php diff --git a/dev/tests/unit/Magento/FunctionalTestFramework/Util/ModulePathExtractorTest.php b/dev/tests/unit/Magento/FunctionalTestFramework/Util/ModulePathExtractorTest.php new file mode 100644 index 000000000..d22d0babf --- /dev/null +++ b/dev/tests/unit/Magento/FunctionalTestFramework/Util/ModulePathExtractorTest.php @@ -0,0 +1,73 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace tests\unit\Magento\FunctionalTestFramework\Test\Util; + +use Magento\FunctionalTestingFramework\Util\ModulePathExtractor; +use PHPUnit\Framework\TestCase; + +class ModulePathExtractorTest extends TestCase +{ + /** + * Validate correct module is returned for dev/tests path + * @throws \Exception + */ + public function testGetMagentoModule() + { + $modulePathExtractor = new ModulePathExtractor(); + $this->assertEquals( + '[Analytics]', + $modulePathExtractor->extractModuleName( + "dev/tests/acceptance/tests/functional/Magento/FunctionalTest/[Analytics]/Test/SomeText.xml" + ) + ); + } + + /** + * Validate correct module is returned for extension path + * @throws \Exception + */ + public function testGetExtensionModule() + { + $modulePathExtractor = new ModulePathExtractor(); + $this->assertEquals( + '[Analytics]', + $modulePathExtractor->extractModuleName( + "app/code/Magento/[Analytics]/Test/Mftf/Test/SomeText.xml" + ) + ); + } + + /** + * Validate Magento is returned for dev/tests/acceptance + * @throws \Exception + */ + public function testMagentoModulePath() + { + $modulePathExtractor = new ModulePathExtractor(); + $this->assertEquals( + 'Magento', + $modulePathExtractor->getExtensionPath( + "dev/tests/acceptance/tests/functional/Magento/FunctionalTest/[Analytics]/Test/SomeText.xml" + ) + ); + } + + /** + * Validate correct extension path is returned + * @throws \Exception + */ + public function testExtensionModulePath() + { + $modulePathExtractor = new ModulePathExtractor(); + $this->assertEquals( + 'TestExtension', + $modulePathExtractor->getExtensionPath( + "app/code/TestExtension/[Analytics]/Test/Mftf/Test/SomeText.xml" + ) + ); + } +} diff --git a/dev/tests/verification/TestModule/Page/SamplePage.xml b/dev/tests/verification/TestModule/Page/SamplePage.xml index b4bf97896..bf8f99615 100644 --- a/dev/tests/verification/TestModule/Page/SamplePage.xml +++ b/dev/tests/verification/TestModule/Page/SamplePage.xml @@ -8,25 +8,25 @@ <pages xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../src/Magento/FunctionalTestingFramework/Page/etc/PageObject.xsd"> - <page name="SamplePage" url="/{{var1}}/{{var2}}.html" area="storefront" module="SampleTests" parameterized="true"> + <page name="SamplePage" url="/{{var1}}/{{var2}}.html" area="storefront" module="TestModule_Magento" parameterized="true"> <section name="SampleSection"/> </page> - <page name="NoParamPage" url="/page.html" area="storefront" module="SampleTests"> + <page name="NoParamPage" url="/page.html" area="storefront" module="TestModule_Magento"> <section name="SampleSection"/> </page> - <page name="OneParamPage" url="/{{var1}}/page.html" area="storefront" module="SampleTests" parameterized="true"> + <page name="OneParamPage" url="/{{var1}}/page.html" area="storefront" module="TestModule_Magento" parameterized="true"> <section name="SampleSection"/> </page> - <page name="TwoParamPage" url="/{{var1}}/{{var2}}.html" area="storefront" module="SampleTests" parameterized="true"> + <page name="TwoParamPage" url="/{{var1}}/{{var2}}.html" area="storefront" module="TestModule_Magento" parameterized="true"> <section name="SampleSection"/> </page> - <page name="AdminPage" url="/backend" area="admin" module="SampleTests"> + <page name="AdminPage" url="/backend" area="admin" module="TestModule_Magento"> <section name="SampleSection"/> </page> - <page name="AdminOneParamPage" url="/{{var1}}/page.html" area="admin" module="SampleTests" parameterized="true"> + <page name="AdminOneParamPage" url="/{{var1}}/page.html" area="admin" module="TestModule_Magento" parameterized="true"> <section name="SampleSection"/> </page> - <page name="ExternalPage" url="http://myFullUrl.com/" area="external" module="SampleTests"> + <page name="ExternalPage" url="http://myFullUrl.com/" area="external" module="TestModule_Magento"> <section name="SampleSection"/> </page> </pages> diff --git a/etc/di.xml b/etc/di.xml index d03ed2267..5d36928f3 100644 --- a/etc/di.xml +++ b/etc/di.xml @@ -78,11 +78,12 @@ <argument name="schemaPath" xsi:type="string">Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd</argument> </arguments> </virtualType> - <virtualType name="Magento\FunctionalTestingFramework\Config\Reader\Page" type="Magento\FunctionalTestingFramework\Config\Reader\Filesystem"> + <virtualType name="Magento\FunctionalTestingFramework\Config\Reader\Page" type="Magento\FunctionalTestingFramework\Config\Reader\MftfFilesystem"> <arguments> <argument name="fileResolver" xsi:type="object">Magento\FunctionalTestingFramework\Config\FileResolver\Module</argument> <argument name="converter" xsi:type="object">Magento\FunctionalTestingFramework\Config\Converter</argument> <argument name="schemaLocator" xsi:type="object">Magento\FunctionalTestingFramework\Config\SchemaLocator\Page</argument> + <argument name="domDocumentClass" xsi:type="string">Magento\FunctionalTestingFramework\Page\Config\Dom</argument> <argument name="idAttributes" xsi:type="array"> <item name="/pages/page" xsi:type="string">name</item> <item name="/pages/page/section" xsi:type="string">name</item> @@ -207,7 +208,7 @@ <argument name="schemaPath" xsi:type="string">Magento/FunctionalTestingFramework/Test/etc/mergedTestSchema.xsd</argument> </arguments> </virtualType> - <virtualType name="Magento\FunctionalTestingFramework\Config\Reader\TestData" type="Magento\FunctionalTestingFramework\Test\Config\Reader\Filesystem"> + <virtualType name="Magento\FunctionalTestingFramework\Config\Reader\TestData" type="Magento\FunctionalTestingFramework\Config\Reader\MftfFilesystem"> <arguments> <argument name="fileResolver" xsi:type="object">Magento\FunctionalTestingFramework\Config\FileResolver\Module</argument> <argument name="converter" xsi:type="object">Magento\FunctionalTestingFramework\Config\TestDataConverter</argument> @@ -286,7 +287,7 @@ </arguments> </virtualType> - <virtualType name="Magento\FunctionalTestingFramework\Config\Reader\ActionGroupData" type="Magento\FunctionalTestingFramework\Test\Config\Reader\Filesystem"> + <virtualType name="Magento\FunctionalTestingFramework\Config\Reader\ActionGroupData" type="Magento\FunctionalTestingFramework\Config\Reader\MftfFilesystem"> <arguments> <argument name="fileResolver" xsi:type="object">Magento\FunctionalTestingFramework\Config\FileResolver\Module</argument> <argument name="converter" xsi:type="object">Magento\FunctionalTestingFramework\Config\ActionGroupDataConverter</argument> diff --git a/src/Magento/FunctionalTestingFramework/Config/Dom.php b/src/Magento/FunctionalTestingFramework/Config/Dom.php index e46d24214..23ec6d2eb 100644 --- a/src/Magento/FunctionalTestingFramework/Config/Dom.php +++ b/src/Magento/FunctionalTestingFramework/Config/Dom.php @@ -94,11 +94,13 @@ public function __construct( * Merge $xml into DOM document * * @param string $xml + * @param string $filename + * @param ExceptionCollector $exceptionCollector * @return void */ - public function merge($xml) + public function merge($xml, $filename = null, $exceptionCollector = null) { - $dom = $this->initDom($xml); + $dom = $this->initDom($xml, $filename, $exceptionCollector); $this->mergeNode($dom->documentElement, ''); } diff --git a/src/Magento/FunctionalTestingFramework/Test/Config/Reader/Filesystem.php b/src/Magento/FunctionalTestingFramework/Config/Reader/MftfFilesystem.php similarity index 94% rename from src/Magento/FunctionalTestingFramework/Test/Config/Reader/Filesystem.php rename to src/Magento/FunctionalTestingFramework/Config/Reader/MftfFilesystem.php index d96a926c5..e39e39199 100644 --- a/src/Magento/FunctionalTestingFramework/Test/Config/Reader/Filesystem.php +++ b/src/Magento/FunctionalTestingFramework/Config/Reader/MftfFilesystem.php @@ -4,12 +4,12 @@ * See COPYING.txt for license details. */ -namespace Magento\FunctionalTestingFramework\Test\Config\Reader; +namespace Magento\FunctionalTestingFramework\Config\Reader; use Magento\FunctionalTestingFramework\Exceptions\Collector\ExceptionCollector; use Magento\FunctionalTestingFramework\Util\Iterator\File; -class Filesystem extends \Magento\FunctionalTestingFramework\Config\Reader\Filesystem +class MftfFilesystem extends \Magento\FunctionalTestingFramework\Config\Reader\Filesystem { /** * Method to redirect file name passing into Dom class diff --git a/src/Magento/FunctionalTestingFramework/Page/Config/Dom.php b/src/Magento/FunctionalTestingFramework/Page/Config/Dom.php new file mode 100644 index 000000000..879622ced --- /dev/null +++ b/src/Magento/FunctionalTestingFramework/Page/Config/Dom.php @@ -0,0 +1,84 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\FunctionalTestingFramework\Page\Config; + +use Magento\FunctionalTestingFramework\Config\MftfApplicationConfig; +use Magento\FunctionalTestingFramework\Exceptions\Collector\ExceptionCollector; +use Magento\FunctionalTestingFramework\Config\Dom\NodeMergingConfig; +use Magento\FunctionalTestingFramework\Config\Dom\NodePathMatcher; +use Magento\FunctionalTestingFramework\Util\ModulePathExtractor; + +class Dom extends \Magento\FunctionalTestingFramework\Config\Dom +{ + /** + * Module Path extractor + * + * @var ModulePathExtractor + */ + private $modulePathExtractor; + + /** + * TestDom constructor. + * @param string $xml + * @param string $filename + * @param ExceptionCollector $exceptionCollector + * @param array $idAttributes + * @param string $typeAttributeName + * @param string $schemaFile + * @param string $errorFormat + */ + public function __construct( + $xml, + $filename, + $exceptionCollector, + array $idAttributes = [], + $typeAttributeName = null, + $schemaFile = null, + $errorFormat = self::ERROR_FORMAT_DEFAULT + ) { + $this->schemaFile = $schemaFile; + $this->nodeMergingConfig = new NodeMergingConfig(new NodePathMatcher(), $idAttributes); + $this->typeAttributeName = $typeAttributeName; + $this->errorFormat = $errorFormat; + $this->modulePathExtractor = new ModulePathExtractor(); + $this->dom = $this->initDom($xml, $filename, $exceptionCollector); + $this->rootNamespace = $this->dom->lookupNamespaceUri($this->dom->namespaceURI); + } + + /** + * Takes a dom element from xml and appends the filename based on location + * + * @param string $xml + * @param string|null $filename + * @param ExceptionCollector $exceptionCollector + * @return \DOMDocument + */ + public function initDom($xml, $filename = null, $exceptionCollector = null) + { + $dom = parent::initDom($xml); + + $pageNodes = $dom->getElementsByTagName('page'); + $currentModule = + $this->modulePathExtractor->extractModuleName($filename) . + '_' . + $this->modulePathExtractor->getExtensionPath($filename); + foreach ($pageNodes as $pageNode) { + $pageModule = $pageNode->getAttribute("module"); + $pageName = $pageNode->getAttribute("name"); + if ($pageModule !== $currentModule) { + if (MftfApplicationConfig::getConfig()->verboseEnabled()) { + print( + "Page Module does not match path Module. " . + "(Page, Module): ($pageName, $pageModule) - Path Module: $currentModule" . + PHP_EOL + ); + } + } + } + return $dom; + } +} diff --git a/src/Magento/FunctionalTestingFramework/Test/Util/TestObjectExtractor.php b/src/Magento/FunctionalTestingFramework/Test/Util/TestObjectExtractor.php index 45f13aed6..5e58c4d53 100644 --- a/src/Magento/FunctionalTestingFramework/Test/Util/TestObjectExtractor.php +++ b/src/Magento/FunctionalTestingFramework/Test/Util/TestObjectExtractor.php @@ -10,6 +10,7 @@ use Magento\FunctionalTestingFramework\Exceptions\XmlException; use Magento\FunctionalTestingFramework\Test\Objects\ActionObject; use Magento\FunctionalTestingFramework\Test\Objects\TestObject; +use Magento\FunctionalTestingFramework\Util\ModulePathExtractor; use Magento\FunctionalTestingFramework\Util\Validation\NameValidationUtil; /** @@ -48,6 +49,13 @@ class TestObjectExtractor extends BaseObjectExtractor */ private $testHookObjectExtractor; + /** + * Module Path extractor + * + * @var ModulePathExtractor + */ + private $modulePathExtractor; + /** * TestObjectExtractor constructor. */ @@ -56,6 +64,7 @@ public function __construct() $this->actionObjectExtractor = new ActionObjectExtractor(); $this->annotationExtractor = new AnnotationExtractor(); $this->testHookObjectExtractor = new TestHookObjectExtractor(); + $this->modulePathExtractor = new ModulePathExtractor(); } /** @@ -76,7 +85,7 @@ public function extractTestData($testData) $filename = $testData['filename'] ?? null; $fileNames = explode(",", $filename); $baseFileName = $fileNames[0]; - $module = $this->extractModuleName($baseFileName); + $module = $this->modulePathExtractor->extractModuleName($baseFileName); $testReference = $testData['extends'] ?? null; $testActions = $this->stripDescriptorTags( $testData, @@ -136,25 +145,4 @@ public function extractTestData($testData) throw new XmlException($exception->getMessage() . ' in Test ' . $filename); } } - - /** - * Extracts module name from the path given - * @param string $path - * @return string - */ - private function extractModuleName($path) - { - if (empty($path)) { - return "NO MODULE DETECTED"; - } - $paths = explode(DIRECTORY_SEPARATOR, $path); - if (count($paths) < 3) { - return "NO MODULE DETECTED"; - } elseif ($paths[count($paths)-3] == "Mftf") { - // app/code/Magento/[Analytics]/Test/Mftf/Test/SomeText.xml - return $paths[count($paths)-5]; - } - // dev/tests/acceptance/tests/functional/Magento/FunctionalTest/[Analytics]/Test/SomeText.xml - return $paths[count($paths)-3]; - } } diff --git a/src/Magento/FunctionalTestingFramework/Util/ModulePathExtractor.php b/src/Magento/FunctionalTestingFramework/Util/ModulePathExtractor.php new file mode 100644 index 000000000..33e559ea8 --- /dev/null +++ b/src/Magento/FunctionalTestingFramework/Util/ModulePathExtractor.php @@ -0,0 +1,51 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\FunctionalTestingFramework\Util; + +/** + * Class ModulePathExtractor, resolve module reference based on path + */ +class ModulePathExtractor +{ + const MAGENTO = 'Magento'; + + /** + * Extracts module name from the path given + * @param string $path + * @return string + */ + public function extractModuleName($path) + { + if (empty($path)) { + return "NO MODULE DETECTED"; + } + $paths = explode(DIRECTORY_SEPARATOR, $path); + if (count($paths) < 3) { + return "NO MODULE DETECTED"; + } elseif ($paths[count($paths)-3] == "Mftf") { + // app/code/Magento/[Analytics]/Test/Mftf/Test/SomeText.xml + return $paths[count($paths)-5]; + } + // dev/tests/acceptance/tests/functional/Magento/FunctionalTest/[Analytics]/Test/SomeText.xml + return $paths[count($paths)-3]; + } + + /** + * Extracts the extension form the path, Magento for dev/tests/acceptance, [name] before module otherwise + * @param string $path + * @return string + */ + public function getExtensionPath($path) + { + $paths = explode(DIRECTORY_SEPARATOR, $path); + if ($paths[count($paths)-3] == "Mftf") { + // app/code/[Magento]/Analytics/Test/Mftf/Test/SomeText.xml + return $paths[count($paths)-6]; + } + return self::MAGENTO; + } +} diff --git a/src/Magento/FunctionalTestingFramework/Util/ModuleResolver.php b/src/Magento/FunctionalTestingFramework/Util/ModuleResolver.php index 275f8fc57..6ab136a52 100644 --- a/src/Magento/FunctionalTestingFramework/Util/ModuleResolver.php +++ b/src/Magento/FunctionalTestingFramework/Util/ModuleResolver.php @@ -238,8 +238,8 @@ private function aggregateTestModulePaths() $codePathsToPattern = [ $modulePath => '', - $appCodePath => '/Test/Mftf', - $vendorCodePath => '/Test/Mftf' + $appCodePath => DIRECTORY_SEPARATOR . 'Test' . DIRECTORY_SEPARATOR . 'Mftf', + $vendorCodePath => DIRECTORY_SEPARATOR . 'Test' . DIRECTORY_SEPARATOR . 'Mftf' ]; foreach ($codePathsToPattern as $codePath => $pattern) { @@ -284,7 +284,7 @@ private function globRelevantPaths($testPath, $pattern) */ private static function globRelevantWrapper($testPath, $pattern) { - return glob($testPath . '*/*' . $pattern); + return glob($testPath . '*' . DIRECTORY_SEPARATOR . '*' . $pattern); } /** From 7525790a83f4778e08fa51e7d7794fb2e9331403 Mon Sep 17 00:00:00 2001 From: Ian Meron <imeron@magento.com> Date: Thu, 31 May 2018 13:39:28 -0500 Subject: [PATCH 043/111] MQE-1024: Refactor Algorithm For Mftf Build Groups - update sorting functions to account for specific action time - update sorting functions to never exceed desired time - update tests following function changes --- .../Util/Sorter/ParallelGroupSorterTest.php | 34 ++++++------ .../Console/GenerateTestsCommand.php | 10 ++-- .../Test/Objects/TestObject.php | 53 ++++++++++++++++--- .../Util/Env/EnvProcessor.php | 2 +- .../Util/Manifest/ParallelTestManifest.php | 8 +-- .../Util/Sorter/ParallelGroupSorter.php | 45 +++++++++------- 6 files changed, 100 insertions(+), 52 deletions(-) diff --git a/dev/tests/unit/Magento/FunctionalTestFramework/Util/Sorter/ParallelGroupSorterTest.php b/dev/tests/unit/Magento/FunctionalTestFramework/Util/Sorter/ParallelGroupSorterTest.php index 1acd31522..c62ce4e4f 100644 --- a/dev/tests/unit/Magento/FunctionalTestFramework/Util/Sorter/ParallelGroupSorterTest.php +++ b/dev/tests/unit/Magento/FunctionalTestFramework/Util/Sorter/ParallelGroupSorterTest.php @@ -36,9 +36,9 @@ public function testBasicTestGroupSort() $expectedResult = [ 1 => ['test2'], 2 => ['test7'], - 3 => ['test6', 'test9'], - 4 => ['test1', 'test4', 'test3'], - 5 => ['test5', 'test10', 'test8'] + 3 => ['test6', 'test4', 'test8'], + 4 => ['test1', 'test9'], + 5 => ['test3', 'test5', 'test10'] ]; $testSorter = new ParallelGroupSorter(); @@ -59,13 +59,16 @@ public function testSortWithSuites() { // mock tests for test object handler. $numberOfCalls = 0; - $mockTest1 = AspectMock::double(TestObject::class, ['getTestActionCount' => function () use (&$numberOfCalls) { - $actionCount = [200, 275]; - $result = $actionCount[$numberOfCalls]; - $numberOfCalls++; - - return $result; - }])->make(); + $mockTest1 = AspectMock::double( + TestObject::class, + ['getEstimatedDuration' => function () use (&$numberOfCalls) { + $actionCount = [300, 275]; + $result = $actionCount[$numberOfCalls]; + $numberOfCalls++; + + return $result; + }] + )->make(); $mockHandler = AspectMock::double( TestObjectHandler::class, @@ -92,17 +95,16 @@ public function testSortWithSuites() // perform sort $testSorter = new ParallelGroupSorter(); - $actualResult = $testSorter->getTestsGroupedBySize($sampleSuiteArray, $sampleTestArray, 200); + $actualResult = $testSorter->getTestsGroupedBySize($sampleSuiteArray, $sampleTestArray, 500); // verify the resulting groups - $this->assertCount(5, $actualResult); + $this->assertCount(4, $actualResult); $expectedResults = [ 1 => ['test3'], - 2 => ['test2'], - 3 => ['mockSuite1_0'], - 4 => ['mockSuite1_1'], - 5 => ['test5', 'test4', 'test1'] + 2 => ['test2','test5', 'test4'], + 3 => ['mockSuite1_0', 'test1'], + 4 => ['mockSuite1_1'] ]; foreach ($actualResult as $groupNum => $group) { diff --git a/src/Magento/FunctionalTestingFramework/Console/GenerateTestsCommand.php b/src/Magento/FunctionalTestingFramework/Console/GenerateTestsCommand.php index a8d34f1c9..7d8cffc35 100644 --- a/src/Magento/FunctionalTestingFramework/Console/GenerateTestsCommand.php +++ b/src/Magento/FunctionalTestingFramework/Console/GenerateTestsCommand.php @@ -35,7 +35,7 @@ protected function configure() ->addArgument('name', InputArgument::OPTIONAL | InputArgument::IS_ARRAY, 'name(s) of specific tests to generate') ->addOption("config", 'c', InputOption::VALUE_REQUIRED, 'default, singleRun, or parallel', 'default') ->addOption("force", 'f',InputOption::VALUE_NONE, 'force generation of tests regardless of Magento Instance Configuration') - ->addOption('lines', 'l', InputOption::VALUE_REQUIRED, 'Used in combination with a parallel configuration, determines desired group size', 500) + ->addOption('time', 'i', InputOption::VALUE_REQUIRED, 'Used in combination with a parallel configuration, determines desired group size (in minutes)', 10) ->addOption('tests', 't', InputOption::VALUE_REQUIRED, 'A parameter accepting a JSON string used to determine the test configuration'); } @@ -45,7 +45,7 @@ protected function configure() * @param InputInterface $input * @param OutputInterface $output * @return void - * @throws \Symfony\Component\Console\Exception\LogicException + * @throws \Symfony\Component\Console\Exception\LogicException|TestFrameworkException */ protected function execute(InputInterface $input, OutputInterface $output) { @@ -53,7 +53,7 @@ protected function execute(InputInterface $input, OutputInterface $output) $config = $input->getOption('config'); $json = $input->getOption('tests'); $force = $input->getOption('force'); - $lines = $input->getOption('lines'); + $time = $input->getOption('time') * 60 * 1000; // convert from minutes to milliseconds $verbose = $output->isVerbose(); if ($json !== null && !json_decode($json)) { @@ -69,13 +69,13 @@ protected function execute(InputInterface $input, OutputInterface $output) if ($config == 'parallel') { /** @var ParallelTestManifest $testManifest */ - $testManifest->createTestGroups($lines); + $testManifest->createTestGroups($time); } SuiteGenerator::getInstance()->generateAllSuites($testManifest); $testManifest->generate(); - print "Generate Tests Command Run" . PHP_EOL; + $output->writeln("Generate Tests Command Run"); } /** diff --git a/src/Magento/FunctionalTestingFramework/Test/Objects/TestObject.php b/src/Magento/FunctionalTestingFramework/Test/Objects/TestObject.php index 7a316fc3c..e8120bddf 100644 --- a/src/Magento/FunctionalTestingFramework/Test/Objects/TestObject.php +++ b/src/Magento/FunctionalTestingFramework/Test/Objects/TestObject.php @@ -15,6 +15,16 @@ */ class TestObject { + const TEST_ACTION_WEIGHT = [ + 'waitForPageLoad' => 1500, + 'amOnPage' => 1000, + 'waitForLoadingMaskToDisappear' => 500, + 'wait' => 'time', + 'comment' => 5, + 'assertCount' => 5, + 'closeAdminNotification' => 10 + ]; + /** * Name of the test * @@ -159,28 +169,57 @@ public function getHooks() } /** - * Returns the number of a test actions contained within a single test (including before/after actions). + * Returns the estimated duration of a single test (including before/after actions). * * @return int */ - public function getTestActionCount() + public function getEstimatedDuration() { // a skipped action results in a single skip being appended to the beginning of the test and no execution if ($this->isSkipped()) { return 1; } - $hookActions = 0; + $hookTime = 0; if (array_key_exists('before', $this->hooks)) { - $hookActions += count($this->hooks['before']->getActions()); + $hookTime += $this->calculateWeightedActionTimes($this->hooks['before']->getActions()); } if (array_key_exists('after', $this->hooks)) { - $hookActions += count($this->hooks['after']->getActions()); + $hookTime += $this->calculateWeightedActionTimes($this->hooks['after']->getActions()); + } + + $testTime = $this->calculateWeightedActionTimes($this->getOrderedActions()); + + return $hookTime + $testTime; + } + + /** + * Function which takes a set of actions and estimates time for completion based on action type. + * + * @param ActionObject[] $actions + * @return int + */ + private function calculateWeightedActionTimes($actions) + { + $actionTime = 0; + // search for any actions of special type + foreach ($actions as $action) { + /** @var ActionObject $action */ + if (array_key_exists($action->getType(), self::TEST_ACTION_WEIGHT)) { + $weight = self::TEST_ACTION_WEIGHT[$action->getType()]; + if (is_string($weight) && $weight === 'time') { + $weight = intval($action->getCustomActionAttributes()[$weight]) * 1000; + } + + $actionTime += $weight; + continue; + } + + $actionTime += 50; } - $testActions = count($this->getOrderedActions()); - return $hookActions + $testActions; + return $actionTime; } /** diff --git a/src/Magento/FunctionalTestingFramework/Util/Env/EnvProcessor.php b/src/Magento/FunctionalTestingFramework/Util/Env/EnvProcessor.php index 53871a098..248e11288 100644 --- a/src/Magento/FunctionalTestingFramework/Util/Env/EnvProcessor.php +++ b/src/Magento/FunctionalTestingFramework/Util/Env/EnvProcessor.php @@ -77,7 +77,7 @@ private function parseEnvFile(): array $envContents = $this->parseEnvFileLines($envFile); } - return array_diff_key($this->parseEnvFileLines($envExampleFile), $envContents); + return array_merge($this->parseEnvFileLines($envExampleFile), $envContents); } private function parseEnvFileLines(array $file): array diff --git a/src/Magento/FunctionalTestingFramework/Util/Manifest/ParallelTestManifest.php b/src/Magento/FunctionalTestingFramework/Util/Manifest/ParallelTestManifest.php index 12ce5d701..c60634bd3 100644 --- a/src/Magento/FunctionalTestingFramework/Util/Manifest/ParallelTestManifest.php +++ b/src/Magento/FunctionalTestingFramework/Util/Manifest/ParallelTestManifest.php @@ -70,22 +70,22 @@ public function __construct($suiteConfiguration, $testPath) */ public function addTest($testObject) { - $this->testNameToSize[$testObject->getCodeceptionName()] = $testObject->getTestActionCount(); + $this->testNameToSize[$testObject->getCodeceptionName()] = $testObject->getEstimatedDuration(); } /** * Function which generates test groups based on arg passed. The function builds groups using the args as an upper * limit. * - * @param int $lines + * @param int $time * @return void */ - public function createTestGroups($lines) + public function createTestGroups($time) { $this->testGroups = $this->parallelGroupSorter->getTestsGroupedBySize( $this->getSuiteConfig(), $this->testNameToSize, - $lines + $time ); $this->suiteConfiguration = $this->parallelGroupSorter->getResultingSuiteConfig(); diff --git a/src/Magento/FunctionalTestingFramework/Util/Sorter/ParallelGroupSorter.php b/src/Magento/FunctionalTestingFramework/Util/Sorter/ParallelGroupSorter.php index 555eb5244..70bda7c4d 100644 --- a/src/Magento/FunctionalTestingFramework/Util/Sorter/ParallelGroupSorter.php +++ b/src/Magento/FunctionalTestingFramework/Util/Sorter/ParallelGroupSorter.php @@ -30,22 +30,22 @@ public function __construct() * * @param array $suiteConfiguration * @param array $testNameToSize - * @param integer $lines + * @param integer $time * @return array * @throws TestFrameworkException */ - public function getTestsGroupedBySize($suiteConfiguration, $testNameToSize, $lines) + public function getTestsGroupedBySize($suiteConfiguration, $testNameToSize, $time) { // we must have the lines argument in order to create the test groups - if ($lines == 0) { + if ($time == 0) { throw new TestFrameworkException( - "Please provide the argument '--lines' to the robo command in order to". + "Please provide the argument '--time' to the robo command in order to". " generate grouped tests manifests for a parallel execution" ); } $testGroups = []; - $splitSuiteNamesToTests = $this->createGroupsWithinSuites($suiteConfiguration, $lines); + $splitSuiteNamesToTests = $this->createGroupsWithinSuites($suiteConfiguration, $time); $splitSuiteNamesToSize = $this->getSuiteToSize($splitSuiteNamesToTests); $entriesForGeneration = array_merge($testNameToSize, $splitSuiteNamesToSize); arsort($entriesForGeneration); @@ -58,7 +58,7 @@ public function getTestsGroupedBySize($suiteConfiguration, $testNameToSize, $lin continue; } - $testGroup = $this->createTestGroup($lines, $testName, $testSize, $testNameToSizeForUse); + $testGroup = $this->createTestGroup($time, $testName, $testSize, $testNameToSizeForUse); $testGroups[$nodeNumber] = $testGroup; // unset the test which have been used. @@ -88,26 +88,29 @@ public function getResultingSuiteConfig() * a test to be used as a starting point, the size of a starting test, an array of tests available to be added to * the group. * - * @param integer $lineMaximum + * @param integer $timeMaximum * @param string $testName * @param integer $testSize * @param array $testNameToSizeForUse * @return array */ - private function createTestGroup($lineMaximum, $testName, $testSize, $testNameToSizeForUse) + private function createTestGroup($timeMaximum, $testName, $testSize, $testNameToSizeForUse) { $group[$testName] = $testSize; - if ($testSize < $lineMaximum) { - while (array_sum($group) < $lineMaximum && !empty($testNameToSizeForUse)) { + if ($testSize < $timeMaximum) { + while (array_sum($group) < $timeMaximum && !empty($testNameToSizeForUse)) { $groupSize = array_sum($group); - $lineGoal = $lineMaximum - $groupSize; + $lineGoal = $timeMaximum - $groupSize; $testNameForUse = $this->getClosestLineCount($testNameToSizeForUse, $lineGoal); - $testSizeForUse = $testNameToSizeForUse[$testNameForUse]; + if ($testNameToSizeForUse[$testNameForUse] < $lineGoal) { + $testSizeForUse = $testNameToSizeForUse[$testNameForUse]; + $group[$testNameForUse] = $testSizeForUse; + } + unset($testNameToSizeForUse[$testNameForUse]); - $group[$testNameForUse] = $testSizeForUse; } } @@ -127,8 +130,12 @@ private function getClosestLineCount($testGroup, $desiredValue) $winner = key($testGroup); $closestThreshold = $desiredValue; foreach ($testGroup as $testName => $testValue) { - $testThreshold = abs($desiredValue - $testValue); - if ($closestThreshold > $testThreshold) { + // find the difference between the desired value and test candidate for the group + $testThreshold = $desiredValue - $testValue; + + // if we see that the gap between the desired value is non-negative and lower than the current closest make + // the test the winner. + if ($closestThreshold > $testThreshold && $testThreshold > 0) { $closestThreshold = $testThreshold; $winner = $testName; } @@ -187,7 +194,7 @@ private function getSuiteNameToTestSize($suiteConfiguration) foreach ($test as $testName) { $suiteNameToTestSize[$suite][$testName] = TestObjectHandler::getInstance() ->getObject($testName) - ->getTestActionCount(); + ->getEstimatedDuration(); } } @@ -224,10 +231,10 @@ private function getSuiteToSize($suiteNamesToTests) * * @param string $suiteName * @param array $tests - * @param integer $lineLimit + * @param integer $maxTime * @return array */ - private function splitTestSuite($suiteName, $tests, $lineLimit) + private function splitTestSuite($suiteName, $tests, $maxTime) { arsort($tests); $split_suites = []; @@ -239,7 +246,7 @@ private function splitTestSuite($suiteName, $tests, $lineLimit) continue; } - $group = $this->createTestGroup($lineLimit, $test, $size, $availableTests); + $group = $this->createTestGroup($maxTime, $test, $size, $availableTests); $split_suites["{$suiteName}_${split_count}"] = $group; $this->addSuiteToConfig($suiteName, "{$suiteName}_${split_count}", $group); From db1ce7bc6aa48be45219bd55f524aa3473778d92 Mon Sep 17 00:00:00 2001 From: Ian Meron <imeron@magento.com> Date: Wed, 23 May 2018 13:54:05 -0500 Subject: [PATCH 044/111] MQE-999: Replace all explicit print or echo statements with logging - add LogginUtil and monolog - remove expliclit echo/print statements - update tests - update mftf exceptions to include log statments --- composer.json | 1 + composer.lock | 78 +++++++++++++++ .../Objects/EntityDataObjectTest.php | 19 ++++ .../OperationDataArrayResolverTest.php | 19 ++++ .../Suite/SuiteGeneratorTest.php | 30 +++++- .../Test/Objects/ActionGroupObjectTest.php | 19 ++++ .../Test/Objects/ActionObjectTest.php | 31 +++++- .../Test/Util/ActionMergeUtilTest.php | 18 ++++ .../Test/Util/ActionObjectExtractorTest.php | 46 ++++++--- .../Test/Util/ObjectExtensionUtilTest.php | 83 +++++++++++++--- .../Util/ModuleResolverTest.php | 32 ++++++- dev/tests/unit/Util/TestLoggingUtil.php | 94 +++++++++++++++++++ .../Tests/SuiteGenerationTest.php | 46 +++++++-- .../Objects/EntityDataObject.php | 24 +++-- .../Exceptions/TestReferenceException.php | 12 ++- .../Exceptions/XmlException.php | 12 ++- .../Suite/SuiteGenerator.php | 19 ++-- .../Test/Objects/ActionObject.php | 39 +++++--- .../Test/Util/ActionObjectExtractor.php | 24 ++--- .../Test/Util/ObjectExtensionUtil.php | 29 +++--- .../Util/Logger/LoggingUtil.php | 82 ++++++++++++++++ .../Util/ModuleResolver.php | 40 ++++++-- .../Util/TestGenerator.php | 12 +-- 23 files changed, 690 insertions(+), 119 deletions(-) create mode 100644 dev/tests/unit/Util/TestLoggingUtil.php create mode 100644 src/Magento/FunctionalTestingFramework/Util/Logger/LoggingUtil.php diff --git a/composer.json b/composer.json index 0b88b59e3..8099cd58e 100755 --- a/composer.json +++ b/composer.json @@ -16,6 +16,7 @@ "epfremme/swagger-php": "^2.0", "flow/jsonpath": ">0.2", "fzaninotto/faker": "^1.6", + "monolog/monolog": "^1.0", "mustache/mustache": "~2.5", "symfony/process": "^2.8 || ^3.1 || ^4.0", "vlucas/phpdotenv": "^2.4" diff --git a/composer.lock b/composer.lock index 8b13ba39a..fd17255c6 100644 --- a/composer.lock +++ b/composer.lock @@ -1618,6 +1618,84 @@ ], "time": "2017-05-10T09:20:27+00:00" }, + { + "name": "monolog/monolog", + "version": "1.23.0", + "source": { + "type": "git", + "url": "https://github.com/Seldaek/monolog.git", + "reference": "fd8c787753b3a2ad11bc60c063cff1358a32a3b4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Seldaek/monolog/zipball/fd8c787753b3a2ad11bc60c063cff1358a32a3b4", + "reference": "fd8c787753b3a2ad11bc60c063cff1358a32a3b4", + "shasum": "" + }, + "require": { + "php": ">=5.3.0", + "psr/log": "~1.0" + }, + "provide": { + "psr/log-implementation": "1.0.0" + }, + "require-dev": { + "aws/aws-sdk-php": "^2.4.9 || ^3.0", + "doctrine/couchdb": "~1.0@dev", + "graylog2/gelf-php": "~1.0", + "jakub-onderka/php-parallel-lint": "0.9", + "php-amqplib/php-amqplib": "~2.4", + "php-console/php-console": "^3.1.3", + "phpunit/phpunit": "~4.5", + "phpunit/phpunit-mock-objects": "2.3.0", + "ruflin/elastica": ">=0.90 <3.0", + "sentry/sentry": "^0.13", + "swiftmailer/swiftmailer": "^5.3|^6.0" + }, + "suggest": { + "aws/aws-sdk-php": "Allow sending log messages to AWS services like DynamoDB", + "doctrine/couchdb": "Allow sending log messages to a CouchDB server", + "ext-amqp": "Allow sending log messages to an AMQP server (1.0+ required)", + "ext-mongo": "Allow sending log messages to a MongoDB server", + "graylog2/gelf-php": "Allow sending log messages to a GrayLog2 server", + "mongodb/mongodb": "Allow sending log messages to a MongoDB server via PHP Driver", + "php-amqplib/php-amqplib": "Allow sending log messages to an AMQP server using php-amqplib", + "php-console/php-console": "Allow sending log messages to Google Chrome", + "rollbar/rollbar": "Allow sending log messages to Rollbar", + "ruflin/elastica": "Allow sending log messages to an Elastic Search server", + "sentry/sentry": "Allow sending log messages to a Sentry server" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Monolog\\": "src/Monolog" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be", + "homepage": "http://seld.be" + } + ], + "description": "Sends your logs to files, sockets, inboxes, databases and various web services", + "homepage": "http://github.com/Seldaek/monolog", + "keywords": [ + "log", + "logging", + "psr-3" + ], + "time": "2017-06-19T01:22:40+00:00" + }, { "name": "moontoast/math", "version": "1.1.2", diff --git a/dev/tests/unit/Magento/FunctionalTestFramework/DataGenerator/Objects/EntityDataObjectTest.php b/dev/tests/unit/Magento/FunctionalTestFramework/DataGenerator/Objects/EntityDataObjectTest.php index 1b611dba6..ab6d52135 100644 --- a/dev/tests/unit/Magento/FunctionalTestFramework/DataGenerator/Objects/EntityDataObjectTest.php +++ b/dev/tests/unit/Magento/FunctionalTestFramework/DataGenerator/Objects/EntityDataObjectTest.php @@ -7,6 +7,7 @@ namespace Magento\FunctionalTestingFramework\DataGenerator\Objects; use PHPUnit\Framework\TestCase; +use tests\unit\Util\TestLoggingUtil; /** * The following function declarations override the global function_exists and declare msq/msqs for use @@ -34,6 +35,15 @@ function msqs($id = null) */ class EntityDataObjectTest extends TestCase { + /** + * Before test functionality + * @return void + */ + public function setUp() + { + TestLoggingUtil::getInstance()->setMockLoggingUtil(); + } + public function testBasicGetters() { $data = ["datakey1" => "value1"]; @@ -109,4 +119,13 @@ public function testGetLinkedEntities() $this->assertEquals("linkedEntity1", $dataObject->getLinkedEntitiesOfType("linkedEntityType")[0]); $this->assertEquals("linkedEntity2", $dataObject->getLinkedEntitiesOfType("otherEntityType")[0]); } + + /** + * After class functionality + * @return void + */ + public static function tearDownAfterClass() + { + TestLoggingUtil::getInstance()->clearMockLoggingUtil(); + } } diff --git a/dev/tests/unit/Magento/FunctionalTestFramework/DataGenerator/Persist/OperationDataArrayResolverTest.php b/dev/tests/unit/Magento/FunctionalTestFramework/DataGenerator/Persist/OperationDataArrayResolverTest.php index 4b57a2f73..ea873c2e9 100644 --- a/dev/tests/unit/Magento/FunctionalTestFramework/DataGenerator/Persist/OperationDataArrayResolverTest.php +++ b/dev/tests/unit/Magento/FunctionalTestFramework/DataGenerator/Persist/OperationDataArrayResolverTest.php @@ -13,6 +13,7 @@ use tests\unit\Util\EntityDataObjectBuilder; use tests\unit\Util\OperationDefinitionBuilder; use tests\unit\Util\OperationElementBuilder; +use tests\unit\Util\TestLoggingUtil; class OperationDataArrayResolverTest extends TestCase { @@ -35,6 +36,15 @@ class OperationDataArrayResolverTest extends TestCase ] ]]; + /** + * Before test functionality + * @return void + */ + public function setUp() + { + TestLoggingUtil::getInstance()->setMockLoggingUtil(); + } + /** * Test a basic metadata resolve between primitive values and a primitive data set * <object> @@ -344,4 +354,13 @@ public function testNestedMetadataArrayOfValue() // Do assert on result here $this->assertEquals(self::NESTED_METADATA_ARRAY_RESULT, $result); } + + /** + * After class functionality + * @return void + */ + public static function tearDownAfterClass() + { + TestLoggingUtil::getInstance()->clearMockLoggingUtil(); + } } diff --git a/dev/tests/unit/Magento/FunctionalTestFramework/Suite/SuiteGeneratorTest.php b/dev/tests/unit/Magento/FunctionalTestFramework/Suite/SuiteGeneratorTest.php index d6790f00e..a45ec9f32 100644 --- a/dev/tests/unit/Magento/FunctionalTestFramework/Suite/SuiteGeneratorTest.php +++ b/dev/tests/unit/Magento/FunctionalTestFramework/Suite/SuiteGeneratorTest.php @@ -19,6 +19,7 @@ use PHPUnit\Framework\TestCase; use tests\unit\Util\SuiteDataArrayBuilder; use tests\unit\Util\TestDataArrayBuilder; +use tests\unit\Util\TestLoggingUtil; class SuiteGeneratorTest extends TestCase { @@ -34,6 +35,15 @@ public static function setUpBeforeClass() ]); } + /** + * Before test functionality + * @return void + */ + public function setUp() + { + TestLoggingUtil::getInstance()->setMockLoggingUtil(); + } + /** * Tests generating a single suite given a set of parsed test data * @throws \Exception @@ -63,7 +73,11 @@ public function testGenerateSuite() $mockSuiteGenerator->generateSuite("basicTestSuite"); // assert that expected suite is generated - $this->expectOutputString("Suite basicTestSuite generated to _generated/basicTestSuite." . PHP_EOL); + TestLoggingUtil::getInstance()->validateMockLogStatement( + 'info', + "suite generated", + ['suite' => 'basicTestSuite', 'relative_path' => "_generated/basicTestSuite"] + ); } /** @@ -96,7 +110,11 @@ public function testGenerateAllSuites() $mockSuiteGenerator->generateAllSuites($exampleTestManifest); // assert that expected suites are generated - $this->expectOutputString("Suite basicTestSuite generated to _generated/basicTestSuite." . PHP_EOL); + TestLoggingUtil::getInstance()->validateMockLogStatement( + 'info', + "suite generated", + ['suite' => 'basicTestSuite', 'relative_path' => "_generated/basicTestSuite"] + ); } /** @@ -181,4 +199,12 @@ private function setMockTestAndSuiteParserOutput($testData, $suiteData) $property->setAccessible(true); $property->setValue($instance, $instance); } + + /** + * clean up function runs after all tests + */ + public static function tearDownAfterClass() + { + TestLoggingUtil::getInstance()->clearMockLoggingUtil(); + } } diff --git a/dev/tests/unit/Magento/FunctionalTestFramework/Test/Objects/ActionGroupObjectTest.php b/dev/tests/unit/Magento/FunctionalTestFramework/Test/Objects/ActionGroupObjectTest.php index 8a5ba7100..a057ba074 100644 --- a/dev/tests/unit/Magento/FunctionalTestFramework/Test/Objects/ActionGroupObjectTest.php +++ b/dev/tests/unit/Magento/FunctionalTestFramework/Test/Objects/ActionGroupObjectTest.php @@ -18,11 +18,21 @@ use PHPUnit\Framework\TestCase; use tests\unit\Util\ActionGroupObjectBuilder; use tests\unit\Util\EntityDataObjectBuilder; +use tests\unit\Util\TestLoggingUtil; class ActionGroupObjectTest extends TestCase { const ACTION_GROUP_MERGE_KEY = 'TestKey'; + /** + * Before test functionality + * @return void + */ + public function setUp() + { + TestLoggingUtil::getInstance()->setMockLoggingUtil(); + } + /** * Tests a string literal in an action group */ @@ -284,4 +294,13 @@ private function assertOnMergeKeyAndActionValue($actions, $expectedValue, $expec $this->assertEquals($expectedMergeKey, $action->getStepKey()); $this->assertEquals($expectedValue, $action->getCustomActionAttributes()); } + + /** + * After class functionality + * @return void + */ + public static function tearDownAfterClass() + { + TestLoggingUtil::getInstance()->clearMockLoggingUtil(); + } } diff --git a/dev/tests/unit/Magento/FunctionalTestFramework/Test/Objects/ActionObjectTest.php b/dev/tests/unit/Magento/FunctionalTestFramework/Test/Objects/ActionObjectTest.php index 8e3a1d579..35339eb93 100644 --- a/dev/tests/unit/Magento/FunctionalTestFramework/Test/Objects/ActionObjectTest.php +++ b/dev/tests/unit/Magento/FunctionalTestFramework/Test/Objects/ActionObjectTest.php @@ -16,12 +16,22 @@ use Magento\FunctionalTestingFramework\Page\Handlers\SectionObjectHandler; use Magento\FunctionalTestingFramework\Page\Objects\SectionObject; use PHPUnit\Framework\TestCase; +use tests\unit\Util\TestLoggingUtil; /** * Class ActionObjectTest */ class ActionObjectTest extends TestCase { + /** + * Before test functionality + * @return void + */ + public function setUp() + { + TestLoggingUtil::getInstance()->setMockLoggingUtil(); + } + /** * The order offset should be 0 when the action is instantiated with 'before' */ @@ -234,14 +244,16 @@ public function testResolveUrlWithNoAttribute() )->make(); // bypass the private constructor AspectMock::double(PageObjectHandler::class, ['getInstance' => $instance]); - // Expect this warning to get generated - $this->expectOutputString( - "WARNING: Page url attribute not found for {{PageObject}} and is required for amOnPage." . PHP_EOL - ); - // Call the method under test $actionObject->resolveReferences(); + // Expect this warning to get generated + TestLoggingUtil::getInstance()->validateMockLogStatement( + "warning", + "page url attribute not found and is required", + ['action' => $actionObject->getType(), 'url' => '{{PageObject}}', 'stepKey' => $actionObject->getStepKey()] + ); + // Verify $expected = [ 'url' => '{{PageObject}}' @@ -371,4 +383,13 @@ private function mockDataHandlerWithData($dataObject) ->make(); AspectMock::double(DataObjectHandler::class, ['getInstance' => $dataInstance]); } + + /** + * After class functionality + * @return void + */ + public static function tearDownAfterClass() + { + TestLoggingUtil::getInstance()->clearMockLoggingUtil(); + } } diff --git a/dev/tests/unit/Magento/FunctionalTestFramework/Test/Util/ActionMergeUtilTest.php b/dev/tests/unit/Magento/FunctionalTestFramework/Test/Util/ActionMergeUtilTest.php index bae9965dd..5bec686da 100644 --- a/dev/tests/unit/Magento/FunctionalTestFramework/Test/Util/ActionMergeUtilTest.php +++ b/dev/tests/unit/Magento/FunctionalTestFramework/Test/Util/ActionMergeUtilTest.php @@ -13,9 +13,19 @@ use Magento\FunctionalTestingFramework\Test\Util\ActionObjectExtractor; use PHPUnit\Framework\TestCase; use tests\unit\Util\DataObjectHandlerReflectionUtil; +use tests\unit\Util\TestLoggingUtil; class ActionMergeUtilTest extends TestCase { + /** + * Before test functionality + * @return void + */ + public function setUp() + { + TestLoggingUtil::getInstance()->setMockLoggingUtil(); + } + /** * Test to validate actions are properly ordered during a merge. * @@ -161,6 +171,14 @@ public function testInsertWait() 0 ); $this->assertEquals($expected, $actual); + } + /** + * After class functionality + * @return void + */ + public static function tearDownAfterClass() + { + TestLoggingUtil::getInstance()->clearMockLoggingUtil(); } } diff --git a/dev/tests/unit/Magento/FunctionalTestFramework/Test/Util/ActionObjectExtractorTest.php b/dev/tests/unit/Magento/FunctionalTestFramework/Test/Util/ActionObjectExtractorTest.php index 873e622f7..f52d279eb 100644 --- a/dev/tests/unit/Magento/FunctionalTestFramework/Test/Util/ActionObjectExtractorTest.php +++ b/dev/tests/unit/Magento/FunctionalTestFramework/Test/Util/ActionObjectExtractorTest.php @@ -8,6 +8,7 @@ use Magento\FunctionalTestingFramework\Test\Objects\ActionObject; use Magento\FunctionalTestingFramework\Test\Util\ActionObjectExtractor; use PHPUnit\Framework\TestCase; +use tests\unit\Util\TestLoggingUtil; class ActionObjectExtractorTest extends TestCase { @@ -20,6 +21,7 @@ class ActionObjectExtractorTest extends TestCase public function setUp() { $this->testActionObjectExtractor = new ActionObjectExtractor(); + TestLoggingUtil::getInstance()->setMockLoggingUtil(); } /** @@ -42,19 +44,27 @@ public function testBasicActionObjectExtration() public function testInvalidMergeOrderReference() { $invalidArray = $this->createBasicActionObjectArray('invalidTestAction1', 'invalidTestAction1'); - $this->expectException('\Magento\FunctionalTestingFramework\Exceptions\TestReferenceException'); - $expectedExceptionMessage = "Invalid ordering configuration in test TestWithSelfReferencingStepKey with step" . - " key(s):\n\tinvalidTestAction1\n"; - $this->expectExceptionMessage($expectedExceptionMessage); - - $this->testActionObjectExtractor->extractActions($invalidArray, 'TestWithSelfReferencingStepKey'); + try { + $this->testActionObjectExtractor->extractActions($invalidArray, 'TestWithSelfReferencingStepKey'); + } catch (\Exception $e) { + TestLoggingUtil::getInstance()->validateMockLogStatement( + 'error', + 'Line 103: Invalid ordering configuration in test', + [ + 'test' => 'TestWithSelfReferencingStepKey', + 'stepKey' => ['invalidTestAction1'] + ] + ); + + throw $e; + } } /** * Validates a warning is printed to the console when multiple actions reference the same actions for merging. */ - public function testAmbiguousMergeOrderRefernece() + public function testAmbiguousMergeOrderReference() { $ambiguousArray = $this->createBasicActionObjectArray('testAction1'); $ambiguousArray = array_merge( @@ -67,12 +77,16 @@ public function testAmbiguousMergeOrderRefernece() $this->createBasicActionObjectArray('testAction3', null, 'testAction1') ); - $outputString = "multiple actions referencing step key testAction1 in test AmbiguousRefTest:\n" . - "\ttestAction2\n" . - "\ttestAction3\n"; - - $this->expectOutputString($outputString); $this->testActionObjectExtractor->extractActions($ambiguousArray, 'AmbiguousRefTest'); + TestLoggingUtil::getInstance()->validateMockLogStatement( + 'warning', + 'multiple actions referencing step key', + [ + 'test' => 'AmbiguousRefTest', + 'stepKey' => 'testAction1', + 'ref' => ['testAction2', 'testAction3'] + ] + ); } /** @@ -103,4 +117,12 @@ private function createBasicActionObjectArray($stepKey = 'testAction1', $before return $baseArray; } + + /** + * clean up function runs after all tests + */ + public static function tearDownAfterClass() + { + TestLoggingUtil::getInstance()->clearMockLoggingUtil(); + } } diff --git a/dev/tests/unit/Magento/FunctionalTestFramework/Test/Util/ObjectExtensionUtilTest.php b/dev/tests/unit/Magento/FunctionalTestFramework/Test/Util/ObjectExtensionUtilTest.php index ddddf6d4b..4b350e5a6 100644 --- a/dev/tests/unit/Magento/FunctionalTestFramework/Test/Util/ObjectExtensionUtilTest.php +++ b/dev/tests/unit/Magento/FunctionalTestFramework/Test/Util/ObjectExtensionUtilTest.php @@ -5,6 +5,7 @@ */ namespace tests\unit\Magento\FunctionalTestFramework\Test\Util; +use AspectMock\Proxy\Verifier; use AspectMock\Test as AspectMock; use Magento\FunctionalTestingFramework\ObjectManager\ObjectManager; use Magento\FunctionalTestingFramework\ObjectManagerFactory; @@ -12,11 +13,24 @@ use Magento\FunctionalTestingFramework\Test\Handlers\TestObjectHandler; use Magento\FunctionalTestingFramework\Test\Parsers\ActionGroupDataParser; use Magento\FunctionalTestingFramework\Test\Parsers\TestDataParser; +use Magento\FunctionalTestingFramework\Util\Logger\LoggingUtil; +use Monolog\Handler\TestHandler; +use Monolog\Logger; use PHPUnit\Framework\TestCase; use tests\unit\Util\TestDataArrayBuilder; +use tests\unit\Util\TestLoggingUtil; class ObjectExtensionUtilTest extends TestCase { + /** + * Before test functionality + * @return void + */ + public function setUp() + { + TestLoggingUtil::getInstance()->setMockLoggingUtil(); + } + /** * Tests generating a test that extends another test * @throws \Exception @@ -41,11 +55,16 @@ public function testGenerateExtendedTest() $mockTestData = ['tests' => array_merge($mockSimpleTest, $mockExtendedTest)]; $this->setMockTestOutput($mockTestData); - $this->expectOutputString("Extending Test: simpleTest => extendedTest" . PHP_EOL); - // parse and generate test object with mocked data $testObject = TestObjectHandler::getInstance()->getObject('extendedTest'); + // assert log statement is correct + TestLoggingUtil::getInstance()->validateMockLogStatement( + 'debug', + 'extending test', + ['parent' => 'simpleTest', 'test' => 'extendedTest'] + ); + // assert that expected test is generated $this->assertEquals($testObject->getParentName(), "simpleTest"); $this->assertArrayHasKey("mockStep", $testObject->getOrderedActions()); @@ -79,11 +98,16 @@ public function testGenerateExtendedWithHooks() $mockTestData = ['tests' => array_merge($mockSimpleTest, $mockExtendedTest)]; $this->setMockTestOutput($mockTestData); - $this->expectOutputString("Extending Test: simpleTest => extendedTest" . PHP_EOL); - // parse and generate test object with mocked data $testObject = TestObjectHandler::getInstance()->getObject('extendedTest'); + // assert log statement is correct + TestLoggingUtil::getInstance()->validateMockLogStatement( + 'debug', + 'extending test', + ['parent' => 'simpleTest', 'test' => 'extendedTest'] + ); + // assert that expected test is generated $this->assertEquals($testObject->getParentName(), "simpleTest"); $this->assertArrayHasKey("mockStepBefore", $testObject->getHooks()['before']->getActions()); @@ -105,10 +129,15 @@ public function testExtendedTestNoParent() $mockTestData = ['tests' => array_merge($mockExtendedTest)]; $this->setMockTestOutput($mockTestData); - $this->expectOutputString("Parent Test simpleTest not defined for Test extendedTest. Skipping Test." . PHP_EOL); - // parse and generate test object with mocked data TestObjectHandler::getInstance()->getObject('extendedTest'); + + // validate log statement + TestLoggingUtil::getInstance()->validateMockLogStatement( + 'debug', + "parent test not defined. test will be skipped", + ['parent' => 'simpleTest', 'test' => 'extendedTest'] + ); } /** @@ -137,11 +166,18 @@ public function testExtendingExtendedTest() $mockTestData = ['tests' => array_merge($mockParentTest, $mockSimpleTest, $mockExtendedTest)]; $this->setMockTestOutput($mockTestData); - $this->expectOutputString("Extending Test: anotherTest => simpleTest" . PHP_EOL); $this->expectExceptionMessage("Cannot extend a test that already extends another test. Test: simpleTest"); // parse and generate test object with mocked data TestObjectHandler::getInstance()->getObject('extendedTest'); + + // validate log statement + TestLoggingUtil::getInstance()->validateMockLogStatement( + 'debug', + "parent test not defined. test will be skipped", + ['parent' => 'simpleTest', 'test' => 'extendedTest'] + ); + $this->expectOutputString("Extending Test: anotherTest => simpleTest" . PHP_EOL); } /** @@ -186,11 +222,16 @@ public function testGenerateExtendedActionGroup() ]; $this->setMockTestOutput(null, $mockActionGroupData); - $this->expectOutputString("Extending Action Group: mockSimpleActionGroup => mockExtendedActionGroup" . PHP_EOL); - // parse and generate test object with mocked data $actionGroupObject = ActionGroupObjectHandler::getInstance()->getObject('mockExtendedActionGroup'); + // validate log statement + TestLoggingUtil::getInstance()->validateMockLogStatement( + 'debug', + 'extending action group:', + ['parent' => $mockSimpleActionGroup['name'], 'actionGroup' => $mockExtendedActionGroup['name']] + ); + // assert that expected test is generated $this->assertEquals("mockSimpleActionGroup", $actionGroupObject->getParentName()); $actions = $actionGroupObject->getActions(); @@ -266,13 +307,24 @@ public function testExtendingExtendedActionGroup() ]; $this->setMockTestOutput(null, $mockActionGroupData); - $this->expectOutputString("Extending Action Group: mockParentActionGroup => mockSimpleActionGroup" . PHP_EOL); $this->expectExceptionMessage( "Cannot extend an action group that already extends another action group. " . $mockSimpleActionGroup['name'] ); // parse and generate test object with mocked data - ActionGroupObjectHandler::getInstance()->getObject('mockExtendedActionGroup'); + try { + ActionGroupObjectHandler::getInstance()->getObject('mockExtendedActionGroup'); + } catch (\Exception $e) { + // validate log statement + TestLoggingUtil::getInstance()->validateMockLogStatement( + 'error', + "Cannot extend an action group that already extends another action group. " . + $mockSimpleActionGroup['name'], + ['parent' => $mockSimpleActionGroup['name'], 'actionGroup' => $mockSimpleActionGroup['name']] + ); + + throw $e; + } } /** @@ -315,4 +367,13 @@ private function setMockTestOutput($testData = null, $actionGroupData = null) // bypass the private constructor AspectMock::double(ObjectManagerFactory::class, ['getObjectManager' => $instance]); } + + /** + * After class functionality + * @return void + */ + public static function tearDownAfterClass() + { + TestLoggingUtil::getInstance()->clearMockLoggingUtil(); + } } diff --git a/dev/tests/unit/Magento/FunctionalTestFramework/Util/ModuleResolverTest.php b/dev/tests/unit/Magento/FunctionalTestFramework/Util/ModuleResolverTest.php index ea670c4a6..0623d47a5 100644 --- a/dev/tests/unit/Magento/FunctionalTestFramework/Util/ModuleResolverTest.php +++ b/dev/tests/unit/Magento/FunctionalTestFramework/Util/ModuleResolverTest.php @@ -11,11 +11,22 @@ use Magento\FunctionalTestingFramework\ObjectManager; use Magento\FunctionalTestingFramework\ObjectManagerFactory; +use Magento\FunctionalTestingFramework\Util\Logger\LoggingUtil; use Magento\FunctionalTestingFramework\Util\ModuleResolver; use PHPUnit\Framework\TestCase; +use tests\unit\Util\TestLoggingUtil; class ModuleResolverTest extends TestCase { + /** + * Before test functionality + * @return void + */ + public function setUp() + { + TestLoggingUtil::getInstance()->setMockLoggingUtil(); + } + /** * remove all registered test doubles */ @@ -24,6 +35,15 @@ protected function tearDown() AspectMock::clean(); } + /** + * After class functionality + * @return void + */ + public static function tearDownAfterClass() + { + TestLoggingUtil::getInstance()->clearMockLoggingUtil(); + } + /** * Validate that Paths that are already set are returned * @throws \Exception @@ -91,10 +111,14 @@ public function testGetModulePathsLocations() public function testGetCustomModulePath() { $this->setMockResolverClass(false, ["Magento_TestModule"], null, null, [], ['otherPath']); - $this->expectOutputString("Including module path: otherPath" . PHP_EOL); $resolver = ModuleResolver::getInstance(); $this->setMockResolverProperties($resolver, null, null, null); $this->assertEquals(['otherPath'], $resolver->getModulesPath()); + TestLoggingUtil::getInstance()->validateMockLogStatement( + 'info', + 'including custom module', + ['module' => 'otherPath'] + ); } /** @@ -119,10 +143,14 @@ function ($arg1, $arg2) { return $mockValue; } ); - $this->expectOutputString("Excluding module: somePath" . PHP_EOL); $resolver = ModuleResolver::getInstance(); $this->setMockResolverProperties($resolver, null, null, ["somePath"]); $this->assertEquals(["otherPath", "lastPath"], $resolver->getModulesPath()); + TestLoggingUtil::getInstance()->validateMockLogStatement( + 'info', + 'excluding module', + ['module' => 'somePath'] + ); } /** diff --git a/dev/tests/unit/Util/TestLoggingUtil.php b/dev/tests/unit/Util/TestLoggingUtil.php new file mode 100644 index 000000000..eb73196d0 --- /dev/null +++ b/dev/tests/unit/Util/TestLoggingUtil.php @@ -0,0 +1,94 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace tests\unit\Util; + +use AspectMock\Test as AspectMock; +use Magento\FunctionalTestingFramework\Util\Logger\LoggingUtil; +use Monolog\Handler\TestHandler; +use Monolog\Logger; +use PHPUnit\Framework\Assert; + +class TestLoggingUtil extends Assert +{ + /** + * @var TestLoggingUtil + */ + private static $INSTANCE; + + /** + * @var TestHandler + */ + private $testLogHandler; + + /** + * TestLoggingUtil constructor. + */ + private function __construct() + { + // private constructor + } + + /** + * Static singleton get function + * + * @return TestLoggingUtil + */ + public static function getInstance() + { + if (self::$INSTANCE == null) { + self::$INSTANCE = new TestLoggingUtil(); + } + + return self::$INSTANCE; + } + + /** + * Function which sets a mock instance of the logger for testing purposes. + * + * @return void + */ + public function setMockLoggingUtil() + { + $this->testLogHandler = new TestHandler(); + $testLogger = new Logger('testLogger'); + $testLogger->pushHandler($this->testLogHandler); + $mockLoggingUtil = AspectMock::double( + LoggingUtil::class, + ['getLogger' => $testLogger] + )->make(); + $property = new \ReflectionProperty(LoggingUtil::class, 'INSTANCE'); + $property->setAccessible(true); + $property->setValue($mockLoggingUtil); + } + + /** + * Function which validates messages have been logged as intended during test execution. + * + * @param string $type + * @param string $message + * @param array $context + */ + public function validateMockLogStatement($type, $message, $context) + { + $records = $this->testLogHandler->getRecords(); + $record = $records[count($records)-1]; // we assume the latest record is what requires validation + $this->assertEquals(strtoupper($type), $record['level_name']); + $this->assertEquals($message, $record['message']); + $this->assertEquals($context, $record['context']); + } + + /** + * Function which clears the test logger context from the LogginUtil class. Should be run after a test class has + * executed. + * + * @return void + */ + public function clearMockLoggingUtil() + { + AspectMock::clean(LoggingUtil::class); + } +} diff --git a/dev/tests/verification/Tests/SuiteGenerationTest.php b/dev/tests/verification/Tests/SuiteGenerationTest.php index 4024329f1..0dd7017c0 100644 --- a/dev/tests/verification/Tests/SuiteGenerationTest.php +++ b/dev/tests/verification/Tests/SuiteGenerationTest.php @@ -13,6 +13,7 @@ use Magento\FunctionalTestingFramework\Util\Manifest\TestManifestFactory; use PHPUnit\Util\Filesystem; use Symfony\Component\Yaml\Yaml; +use tests\unit\Util\TestLoggingUtil; use tests\util\MftfTestCase; class SuiteGenerationTest extends MftfTestCase @@ -52,6 +53,8 @@ public function setUp() realpath(FW_BP . '/etc/config/codeception.dist.yml'), self::CONFIG_YML_FILE ); + + TestLoggingUtil::getInstance()->setMockLoggingUtil(); } /** @@ -71,8 +74,13 @@ public function testSuiteGeneration1() // Generate the Suite SuiteGenerator::getInstance()->generateSuite($groupName); - // Validate console message and add group name for later deletion - $this->expectOutputRegex('/Suite .* generated to .*/'); + // Validate log message and add group name for later deletion + TestLoggingUtil::getInstance()->validateMockLogStatement( + 'info', + "suite generated", + ['suite' => $groupName, 'relative_path' => "_generated/$groupName"] + ); + self::$TEST_GROUPS[] = $groupName; // Validate Yaml file updated @@ -121,8 +129,14 @@ public function testSuiteGenerationParallel() $parallelManifest->createTestGroups(1); SuiteGenerator::getInstance()->generateAllSuites($parallelManifest); - // Validate console message and add group name for later deletion - $this->expectOutputRegex('/Suite .* generated to .*/'); + // Validate log message (for final group) and add group name for later deletion + $expectedGroup = $expectedGroups[count($expectedGroups)-1] ; + TestLoggingUtil::getInstance()->validateMockLogStatement( + 'info', + "suite generated", + ['suite' => $expectedGroup, 'relative_path' => "_generated/$expectedGroup"] + ); + self::$TEST_GROUPS[] = $groupName; // Validate Yaml file updated @@ -158,8 +172,12 @@ public function testSuiteGenerationHooks() // Generate the Suite SuiteGenerator::getInstance()->generateSuite($groupName); - // Validate console message and add group name for later deletion - $this->expectOutputRegex('/Suite .* generated to .*/'); + // Validate log message and add group name for later deletion + TestLoggingUtil::getInstance()->validateMockLogStatement( + 'info', + "suite generated", + ['suite' => $groupName, 'relative_path' => "_generated/$groupName"] + ); self::$TEST_GROUPS[] = $groupName; // Validate Yaml file updated @@ -222,8 +240,12 @@ public function testSuiteGenerationSingleRun() SuiteGenerator::getInstance()->generateAllSuites($singleRunManifest); $singleRunManifest->generate(); - // Validate console message and add group name for later deletion - $this->expectOutputRegex('/Suite .* generated to .*/'); + // Validate log message and add group name for later deletion + TestLoggingUtil::getInstance()->validateMockLogStatement( + 'info', + "suite generated", + ['suite' => $groupName, 'relative_path' => "_generated/$groupName"] + ); self::$TEST_GROUPS[] = $groupName; // Validate Yaml file updated @@ -262,6 +284,14 @@ public function tearDown() ); } + /** + * Remove yml if created during tests and did not exist before + */ + public static function tearDownAfterClass() + { + TestLoggingUtil::getInstance()->clearMockLoggingUtil(); + } + /** * Getter for manifest file path * diff --git a/src/Magento/FunctionalTestingFramework/DataGenerator/Objects/EntityDataObject.php b/src/Magento/FunctionalTestingFramework/DataGenerator/Objects/EntityDataObject.php index d8564a26b..74e232145 100644 --- a/src/Magento/FunctionalTestingFramework/DataGenerator/Objects/EntityDataObject.php +++ b/src/Magento/FunctionalTestingFramework/DataGenerator/Objects/EntityDataObject.php @@ -6,7 +6,9 @@ namespace Magento\FunctionalTestingFramework\DataGenerator\Objects; +use Magento\FunctionalTestingFramework\Config\MftfApplicationConfig; use Magento\FunctionalTestingFramework\Exceptions\TestFrameworkException; +use Magento\FunctionalTestingFramework\Util\Logger\LoggingUtil; /** * Class EntityDataObject @@ -126,10 +128,16 @@ public function getAllData() */ public function getDataByName($name, $uniquenessFormat) { + if (MftfApplicationConfig::getConfig()->verboseEnabled()) { + LoggingUtil::getInstance()->getLogger(EntityDataObject::class) + ->debug("Fetching data field from entity", ["entity" => $this->getName(), "field" => $name]); + } + if (!$this->isValidUniqueDataFormat($uniquenessFormat)) { - throw new TestFrameworkException( - sprintf('Invalid unique data format value: %s \n', $uniquenessFormat) - ); + $exceptionMessage = sprintf("Invalid unique data format value: %s \n", $uniquenessFormat); + LoggingUtil::getInstance()->getLogger(EntityDataObject::class) + ->error($exceptionMessage, ["entity" => $this->getName(), "field" => $name]); + throw new TestFrameworkException($exceptionMessage); } $name_lower = strtolower($name); @@ -204,12 +212,12 @@ private function formatUniqueData($name, $uniqueData, $uniqueDataFormat) private function checkUniquenessFunctionExists($function, $uniqueDataFormat) { if (!function_exists($function)) { - throw new TestFrameworkException( - sprintf( - 'Unique data format value: %s can only be used when running cests.\n', - $uniqueDataFormat - ) + $exceptionMessage = sprintf( + 'Unique data format value: %s can only be used when running cests.\n', + $uniqueDataFormat ); + + throw new TestFrameworkException($exceptionMessage); } } diff --git a/src/Magento/FunctionalTestingFramework/Exceptions/TestReferenceException.php b/src/Magento/FunctionalTestingFramework/Exceptions/TestReferenceException.php index 1fd2e4f67..df871c75f 100644 --- a/src/Magento/FunctionalTestingFramework/Exceptions/TestReferenceException.php +++ b/src/Magento/FunctionalTestingFramework/Exceptions/TestReferenceException.php @@ -6,6 +6,8 @@ namespace Magento\FunctionalTestingFramework\Exceptions; +use Magento\FunctionalTestingFramework\Util\Logger\LoggingUtil; + /** * Class TestReferenceException */ @@ -14,9 +16,17 @@ class TestReferenceException extends \Exception /** * TestReferenceException constructor. * @param string $message + * @param array $context + * @SuppressWarnings(PHPMD.UnusedLocalVariable) */ - public function __construct($message) + public function __construct($message, $context = []) { + list($childClass, $callingClass) = debug_backtrace(false, 2); + LoggingUtil::getInstance()->getLogger($callingClass['class'])->error( + "Line {$callingClass['line']}: $message", + $context + ); + parent::__construct($message); } } diff --git a/src/Magento/FunctionalTestingFramework/Exceptions/XmlException.php b/src/Magento/FunctionalTestingFramework/Exceptions/XmlException.php index 2ee008b79..ac44e1885 100644 --- a/src/Magento/FunctionalTestingFramework/Exceptions/XmlException.php +++ b/src/Magento/FunctionalTestingFramework/Exceptions/XmlException.php @@ -6,6 +6,8 @@ namespace Magento\FunctionalTestingFramework\Exceptions; +use Magento\FunctionalTestingFramework\Util\Logger\LoggingUtil; + /** * Class XmlException */ @@ -14,9 +16,17 @@ class XmlException extends \Exception /** * XmlException constructor. * @param string $message + * @param array $context + * @SuppressWarnings(PHPMD.UnusedLocalVariable) */ - public function __construct($message) + public function __construct($message, $context = []) { + list($childClass, $callingClass) = debug_backtrace(false, 2); + LoggingUtil::getInstance()->getLogger($callingClass['class'])->error( + $message, + $context + ); + parent::__construct($message); } } diff --git a/src/Magento/FunctionalTestingFramework/Suite/SuiteGenerator.php b/src/Magento/FunctionalTestingFramework/Suite/SuiteGenerator.php index c7b31610a..4e0ef32df 100644 --- a/src/Magento/FunctionalTestingFramework/Suite/SuiteGenerator.php +++ b/src/Magento/FunctionalTestingFramework/Suite/SuiteGenerator.php @@ -13,6 +13,7 @@ use Magento\FunctionalTestingFramework\Suite\Objects\SuiteObject; use Magento\FunctionalTestingFramework\Test\Handlers\TestObjectHandler; use Magento\FunctionalTestingFramework\Util\Filesystem\DirSetupUtil; +use Magento\FunctionalTestingFramework\Util\Logger\LoggingUtil; use Magento\FunctionalTestingFramework\Util\Manifest\BaseTestManifest; use Magento\FunctionalTestingFramework\Util\TestGenerator; use Symfony\Component\Yaml\Yaml; @@ -140,7 +141,10 @@ private function generateSuiteFromTest($suiteName, $tests = [], $originalSuiteNa $groupNamespace = $this->generateGroupFile($suiteName, $relevantTests, $originalSuiteName); $this->appendEntriesToConfig($suiteName, $fullPath, $groupNamespace); - print "Suite ${suiteName} generated to ${relativePath}.\n"; + LoggingUtil::getInstance()->getLogger(SuiteGenerator::class)->info( + "suite generated", + ['suite' => $suiteName, 'relative_path' => $relativePath] + ); } /** @@ -158,17 +162,12 @@ private function validateTestsReferencedInSuite($suiteName, $testsReferenced, $o { $suiteRef = $originalSuiteName ?? $suiteName; $possibleTestRef = SuiteObjectHandler::getInstance()->getObject($suiteRef)->getTests(); - $invalidTestRef = null; - $errorMsg = "Cannot reference tests not declared as part of {$suiteRef}:\n "; + $errorMsg = "Cannot reference tests whcih are not declared as part of suite."; - array_walk($testsReferenced, function ($value) use (&$invalidTestRef, $possibleTestRef, &$errorMsg) { - if (!array_key_exists($value, $possibleTestRef)) { - $invalidTestRef.= "\t{$value}\n"; - } - }); + $invalidTestRef = array_diff($testsReferenced, array_keys($possibleTestRef)); - if ($invalidTestRef != null) { - throw new TestReferenceException($errorMsg . $invalidTestRef); + if (!empty($invalidTestRef)) { + throw new TestReferenceException($errorMsg, ['suite' => $suiteRef, 'test' => $invalidTestRef]); } } diff --git a/src/Magento/FunctionalTestingFramework/Test/Objects/ActionObject.php b/src/Magento/FunctionalTestingFramework/Test/Objects/ActionObject.php index e2bd106d1..a7e3117da 100644 --- a/src/Magento/FunctionalTestingFramework/Test/Objects/ActionObject.php +++ b/src/Magento/FunctionalTestingFramework/Test/Objects/ActionObject.php @@ -15,6 +15,7 @@ use Magento\FunctionalTestingFramework\Page\Handlers\PageObjectHandler; use Magento\FunctionalTestingFramework\Page\Handlers\SectionObjectHandler; use Magento\FunctionalTestingFramework\Exceptions\TestReferenceException; +use Magento\FunctionalTestingFramework\Util\Logger\LoggingUtil; /** * Class ActionObject @@ -271,7 +272,10 @@ public function trimAssertionAttributes() // @codingStandardsIgnoreStart $appConfig = MftfApplicationConfig::getConfig(); if ($appConfig->getPhase() == MftfApplicationConfig::GENERATION_PHASE && $appConfig->verboseEnabled()) { - echo("WARNING: Use of one line Assertion actions will be deprecated in MFTF 3.0.0, please use nested syntax (Action: {$this->type} StepKey: {$this->stepKey})" . PHP_EOL); + LoggingUtil::getInstance()->getLogger(ActionObject::class)->warning( + "use of one line Assertion actions will be deprecated in MFTF 3.0.0, please use nested syntax", + ["action" => $this->type, "stepKey" => $this->stepKey] + ); } // @codingStandardsIgnoreEnd return; @@ -318,7 +322,10 @@ private function validateAssertionSchema($attributes) if (!in_array('expectedResult', $attributes) || !in_array('actualResult', $attributes)) { // @codingStandardsIgnoreStart - throw new TestReferenceException("{$this->type} must have both an expectedResult and actualResult defined (stepKey: {$this->stepKey})"); + throw new TestReferenceException( + "{$this->type} must have both an expectedResult and actualResult defined (stepKey: {$this->stepKey})", + ["action" => $this->type, "stepKey" => $this->stepKey] + ); // @codingStandardsIgnoreEnd } } @@ -373,11 +380,12 @@ private function resolveUrlReference() if ($replacement) { $this->resolvedCustomAttributes[ActionObject::ACTION_ATTRIBUTE_URL] = $replacement; $allPages = PageObjectHandler::getInstance()->getAllObjects(); - if ( - $replacement === $url - && array_key_exists(trim($url, "{}"), $allPages) + if ($replacement === $url && array_key_exists(trim($url, "{}"), $allPages) ) { - echo("WARNING: Page url attribute not found for ${url} and is required for $this->type." . PHP_EOL); + LoggingUtil::getInstance()->getLogger(ActionObject::class)->warning( + "page url attribute not found and is required", + ["action" => $this->type, "url" => $url, "stepKey" => $this->stepKey] + ); } } } @@ -505,7 +513,7 @@ private function findAndReplaceReferences($objectHandler, $inputString) } elseif (get_class($obj) == SectionObject::class) { list(,$objField) = $this->stripAndSplitReference($match); if ($obj->getElement($objField) == null) { - throw new TestReferenceException("Could not resolve entity reference " . $inputString); + throw new TestReferenceException("Could not resolve entity reference", ["input" => $inputString]); } $parameterized = $obj->getElement($objField)->isParameterized(); $replacement = $obj->getElement($objField)->getPrioritizedSelector(); @@ -522,7 +530,7 @@ private function findAndReplaceReferences($objectHandler, $inputString) if (get_class($objectHandler) != DataObjectHandler::class) { return $this->findAndReplaceReferences(DataObjectHandler::getInstance(), $outputString); } else { - throw new TestReferenceException("Could not resolve entity reference " . $inputString); + throw new TestReferenceException("Could not resolve entity reference", ["input" => $inputString]); } } @@ -545,12 +553,14 @@ private function validateMutuallyExclusiveAttributes(array $attributes) if (count($matches) > 1) { throw new TestReferenceException( "Actions of type '{$this->getType()}' must only contain one attribute of types '" - . implode("', '", $attributes) . "'" + . implode("', '", $attributes) . "'", + ["type" => $this->getType(), "attributes" => $attributes] ); } elseif (count($matches) == 0) { throw new TestReferenceException( "Actions of type '{$this->getType()}' must contain at least one attribute of types '" - . implode("', '", $attributes) . "'" + . implode("', '", $attributes) . "'", + ["type" => $this->getType(), "attributes" => $attributes] ); } } @@ -567,7 +577,8 @@ private function validateUrlAreaAgainstActionType($obj) if ($obj->getArea() == 'external' && in_array($this->getType(), self::EXTERNAL_URL_AREA_INVALID_ACTIONS)) { throw new TestReferenceException( - "Page of type 'external' is not compatible with action type '{$this->getType()}'" + "Page of type 'external' is not compatible with action type '{$this->getType()}'", + ["type" => $this->getType()] ); } } @@ -640,12 +651,14 @@ private function matchParameterReferences($reference, $parameters) } throw new TestReferenceException( "Parameter Resolution Failed: Not enough parameters given for reference " . - $reference . ". Parameters Given: " . $parametersGiven + $reference . ". Parameters Given: " . $parametersGiven, + ["reference" => $reference, "parametersGiven" => $parametersGiven] ); } elseif (count($varMatches[0]) < count($parameters)) { throw new TestReferenceException( "Parameter Resolution Failed: Too many parameters given for reference " . - $reference . ". Parameters Given: " . implode(", ", $parameters) + $reference . ". Parameters Given: " . implode(", ", $parameters), + ["reference" => $reference, "parametersGiven" => $parameters] ); } diff --git a/src/Magento/FunctionalTestingFramework/Test/Util/ActionObjectExtractor.php b/src/Magento/FunctionalTestingFramework/Test/Util/ActionObjectExtractor.php index 95c6f1501..4ffd70ecf 100644 --- a/src/Magento/FunctionalTestingFramework/Test/Util/ActionObjectExtractor.php +++ b/src/Magento/FunctionalTestingFramework/Test/Util/ActionObjectExtractor.php @@ -11,6 +11,7 @@ use Magento\FunctionalTestingFramework\Exceptions\TestReferenceException; use Magento\FunctionalTestingFramework\Exceptions\XmlException; use Magento\FunctionalTestingFramework\Test\Objects\ActionObject; +use Magento\FunctionalTestingFramework\Util\Logger\LoggingUtil; /** * Class ActionObjectExtractor @@ -243,12 +244,10 @@ private function auditMergeSteps($stepKeyRefs, $testName) }, ARRAY_FILTER_USE_BOTH); if (!empty($invalidStepRef)) { - $errorMsg = "Invalid ordering configuration in test {$testName} with step key(s):\n"; - array_walk($invalidStepRef, function ($value, $key) use (&$errorMsg) { - $errorMsg.="\t{$key}\n"; - }); - - throw new TestReferenceException($errorMsg); + throw new TestReferenceException( + "Invalid ordering configuration in test", + ['test' => $testName, 'stepKey' => array_keys($invalidStepRef)] + ); } // check for ambiguous references to step keys (multiple refs across test merges). @@ -256,16 +255,11 @@ private function auditMergeSteps($stepKeyRefs, $testName) return count($value) > 1; }); - $multipleActionsError = ""; foreach ($atRiskStepRef as $stepKey => $stepRefs) { - $multipleActionsError.= "multiple actions referencing step key {$stepKey} in test {$testName}:\n"; - array_walk($stepRefs, function ($value) use (&$multipleActionsError) { - $multipleActionsError.= "\t{$value}\n"; - }); - } - - if (MftfApplicationConfig::getConfig()->verboseEnabled()) { - print $multipleActionsError; + LoggingUtil::getInstance()->getLogger(ActionObjectExtractor::class)->warn( + 'multiple actions referencing step key', + ['test' => $testName, 'stepKey' => $stepKey, 'ref' => $stepRefs] + ); } } } diff --git a/src/Magento/FunctionalTestingFramework/Test/Util/ObjectExtensionUtil.php b/src/Magento/FunctionalTestingFramework/Test/Util/ObjectExtensionUtil.php index 14a02739d..97f67004a 100644 --- a/src/Magento/FunctionalTestingFramework/Test/Util/ObjectExtensionUtil.php +++ b/src/Magento/FunctionalTestingFramework/Test/Util/ObjectExtensionUtil.php @@ -15,6 +15,7 @@ use Magento\FunctionalTestingFramework\Test\Objects\TestObject; use Magento\FunctionalTestingFramework\Test\Objects\TestHookObject; use Magento\FunctionalTestingFramework\Test\Handlers\TestObjectHandler; +use Magento\FunctionalTestingFramework\Util\Logger\LoggingUtil; class ObjectExtensionUtil { @@ -40,13 +41,10 @@ public function extendTest($testObject) $parentTest = TestObjectHandler::getInstance()->getObject($testObject->getParentName()); } catch (TestReferenceException $error) { if (MftfApplicationConfig::getConfig()->verboseEnabled()) { - echo( - "Parent Test " . - $testObject->getParentName() . - " not defined for Test " . - $testObject->getName() . - ". Skipping Test." . - PHP_EOL); + LoggingUtil::getInstance()->getLogger(ObjectExtensionUtil::class)->debug( + "parent test not defined. test will be skipped", + ["parent" => $testObject->getParentName(), "test" => $testObject->getName()] + ); } $skippedTest = $this->skipTest($testObject); return $skippedTest; @@ -55,11 +53,13 @@ public function extendTest($testObject) // Check to see if the parent test is already an extended test if ($parentTest->getParentName() !== null) { throw new XmlException( - "Cannot extend a test that already extends another test. Test: " . $parentTest->getName() + "Cannot extend a test that already extends another test. Test: " . $parentTest->getName(), + ["parent" => $parentTest->getName(), "actionGroup" => $testObject->getName()] ); } if (MftfApplicationConfig::getConfig()->verboseEnabled()) { - echo("Extending Test: " . $parentTest->getName() . " => " . $testObject->getName() . PHP_EOL); + LoggingUtil::getInstance()->getLogger(ObjectExtensionUtil::class) + ->debug("extending test", ["parent" => $parentTest->getName(), "test" => $testObject->getName()]); } // Get steps for both the parent and the child tests @@ -106,15 +106,14 @@ public function extendActionGroup($actionGroupObject) if ($parentActionGroup->getParentName() !== null) { throw new XmlException( "Cannot extend an action group that already extends another action group. " . - $parentActionGroup->getName() + $parentActionGroup->getName(), + ["parent" => $parentActionGroup->getName(), "actionGroup" => $actionGroupObject->getName()] ); } if (MftfApplicationConfig::getConfig()->verboseEnabled()) { - echo("Extending Action Group: " . - $parentActionGroup->getName() . - " => " . - $actionGroupObject->getName() . - PHP_EOL + LoggingUtil::getInstance()->getLogger(ObjectExtensionUtil::class)->debug( + "extending action group:", + ["parent" => $parentActionGroup->getName(), "actionGroup" => $actionGroupObject->getName()] ); } diff --git a/src/Magento/FunctionalTestingFramework/Util/Logger/LoggingUtil.php b/src/Magento/FunctionalTestingFramework/Util/Logger/LoggingUtil.php new file mode 100644 index 000000000..51a4e5d8c --- /dev/null +++ b/src/Magento/FunctionalTestingFramework/Util/Logger/LoggingUtil.php @@ -0,0 +1,82 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\FunctionalTestingFramework\Util\Logger; + +use Monolog\Handler\StreamHandler; +use Monolog\Logger; + +class LoggingUtil +{ + /** + * Private Map of Logger instances, indexed by Class Name. + * + * @var array + */ + private $loggers = []; + + /** + * Singleton LogginUtil Instance + * + * @var LoggingUtil + */ + private static $INSTANCE; + + /** + * Singleton accessor for instance variable + * + * @return LoggingUtil + */ + public static function getInstance() + { + if (self::$INSTANCE == null) { + self::$INSTANCE = new LoggingUtil(); + } + + return self::$INSTANCE; + } + + /** + * Constructor for Logging Util + */ + private function __construct() + { + // private constructor + } + + /** + * Creates a new logger instances based on class name if it does not exist. If logger instance already exists, the + * existing instance is simply returned. + * + * @param string $clazz + * @return Logger + * @throws \Exception + */ + public function getLogger($clazz) + { + if ($clazz == null) { + throw new \Exception("You must pass a class to receive a logger"); + } + + if (!array_key_exists($clazz, $this->loggers)) { + $logger = new Logger($clazz); + $logger->pushHandler(new StreamHandler($this->getLoggingPath())); + $this->loggers[$clazz] = $logger; + } + + return $this->loggers[$clazz]; + } + + /** + * Function which returns a static path to the the log file. + * + * @return string + */ + private function getLoggingPath() + { + return TESTS_BP . DIRECTORY_SEPARATOR . "mftf.log"; + } +} diff --git a/src/Magento/FunctionalTestingFramework/Util/ModuleResolver.php b/src/Magento/FunctionalTestingFramework/Util/ModuleResolver.php index 6ab136a52..a32486aba 100644 --- a/src/Magento/FunctionalTestingFramework/Util/ModuleResolver.php +++ b/src/Magento/FunctionalTestingFramework/Util/ModuleResolver.php @@ -7,6 +7,7 @@ namespace Magento\FunctionalTestingFramework\Util; use Magento\FunctionalTestingFramework\Config\MftfApplicationConfig; +use Magento\FunctionalTestingFramework\Util\Logger\LoggingUtil; /** * Class ModuleResolver, resolve module path based on enabled modules of target Magento instance. @@ -191,11 +192,13 @@ public function getModulesPath() $enabledModules = $this->getEnabledModules(); if (empty($enabledModules) && !MftfApplicationConfig::getConfig()->forceGenerateEnabled()) { - trigger_error( - "Could not retrieve enabled modules from provided 'MAGENTO_BASE_URL'," . - "please make sure Magento is available at this url", - E_USER_ERROR + $errorMsg = 'Could not retrieve enabled modules from provided MAGENTO_BASE_URL ' . + 'please make sure Magento is available at this url'; + LoggingUtil::getInstance()->getLogger(ModuleResolver::class)->error( + $errorMsg, + ['MAGENTO_BASE_URL' => getenv('MAGENTO_BASE_URL')] ); + trigger_error($errorMsg, E_USER_ERROR); } $allModulePaths = $this->aggregateTestModulePaths(); @@ -270,6 +273,13 @@ private function globRelevantPaths($testPath, $pattern) foreach ($relevantPaths as $codePath) { $mainModName = basename(str_replace($pattern, '', $codePath)); $modulePaths[$mainModName][] = $codePath; + + if (MftfApplicationConfig::getConfig()->verboseEnabled()) { + LoggingUtil::getInstance()->getLogger(ModuleResolver::class)->debug( + "including module", + ['module' => $mainModName, 'path' => $codePath] + ); + } } return $modulePaths; @@ -337,7 +347,10 @@ private function printMagentoVersionInfo() return; } $url = ConfigSanitizerUtil::sanitizeUrl(getenv('MAGENTO_BASE_URL')) . $this->versionUrl; - print "Fetching version information from {$url}\n"; + LoggingUtil::getInstance()->getLogger(ModuleResolver::class)->info( + "Fetching version information.", + ['url' => $url] + ); $ch = curl_init($url); curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "GET"); @@ -348,9 +361,10 @@ private function printMagentoVersionInfo() $response = "No version information available."; } - if (MftfApplicationConfig::getConfig()->verboseEnabled()) { - print "\nVersion Information: {$response}\n"; - } + LoggingUtil::getInstance()->getLogger(ModuleResolver::class)->info( + 'version information', + ['version' => $response] + ); } /** @@ -413,7 +427,10 @@ protected function applyCustomModuleMethods($modulesPath) $customModulePaths = $this->getCustomModulePaths(); array_map(function ($value) { - print "Including module path: {$value}\n"; + LoggingUtil::getInstance()->getLogger(ModuleResolver::class)->info( + "including custom module", + ['module' => $value] + ); }, $customModulePaths); return $this->flattenAllModulePaths(array_merge($modulePathsResult, $customModulePaths)); @@ -431,7 +448,10 @@ private function removeBlacklistModules($modulePaths) foreach ($modulePathsResult as $moduleName => $modulePath) { if (in_array($moduleName, $this->getModuleBlacklist())) { unset($modulePathsResult[$moduleName]); - print "Excluding module: {$moduleName}\n"; + LoggingUtil::getInstance()->getLogger(ModuleResolver::class)->info( + "excluding module", + ['module' => $moduleName] + ); } } diff --git a/src/Magento/FunctionalTestingFramework/Util/TestGenerator.php b/src/Magento/FunctionalTestingFramework/Util/TestGenerator.php index 789aa04f8..c55cfa916 100644 --- a/src/Magento/FunctionalTestingFramework/Util/TestGenerator.php +++ b/src/Magento/FunctionalTestingFramework/Util/TestGenerator.php @@ -16,6 +16,7 @@ use Magento\FunctionalTestingFramework\DataGenerator\Handlers\DataObjectHandler; use Magento\FunctionalTestingFramework\Test\Objects\TestHookObject; use Magento\FunctionalTestingFramework\Test\Objects\TestObject; +use Magento\FunctionalTestingFramework\Util\Logger\LoggingUtil; use Magento\FunctionalTestingFramework\Util\Manifest\BaseTestManifest; use Magento\FunctionalTestingFramework\Util\Manifest\TestManifestFactory; use Magento\FunctionalTestingFramework\Test\Util\ActionObjectExtractor; @@ -130,11 +131,10 @@ private function loadAllTestObjects($testsToIgnore) // them in the current context. $invalidTestObjects = array_intersect_key($this->tests, $testsToIgnore); if (!empty($invalidTestObjects)) { - $errorMsg = "Cannot reference the following tests for generation without accompanying suite:\n"; - array_walk($invalidTestObjects, function ($value, $key) use (&$errorMsg) { - $errorMsg.= "\t{$key}\n"; - }); - throw new TestReferenceException($errorMsg); + throw new TestReferenceException( + "Cannot reference test configuration for generation without accompanying suite.", + ['tests' => array_keys($invalidTestObjects)] + ); } return $this->tests; @@ -155,7 +155,7 @@ private function createCestFile($testPhp, $filename) $file = fopen($exportFilePath, 'w'); if (!$file) { - throw new \Exception("Could not open the file!"); + throw new \Exception("Could not open the file."); } fwrite($file, $testPhp); From ae2fba10f81721b5f1db623592a1993e215bb44b Mon Sep 17 00:00:00 2001 From: Ian Meron <imeron@magento.com> Date: Mon, 4 Jun 2018 11:38:37 -0500 Subject: [PATCH 045/111] MQE-1024: Refactor Algorithm For Mftf Build Groups - fix review comments --- .../Test/Objects/TestObject.php | 6 ++++-- .../Util/Sorter/ParallelGroupSorter.php | 12 ++++++------ 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/src/Magento/FunctionalTestingFramework/Test/Objects/TestObject.php b/src/Magento/FunctionalTestingFramework/Test/Objects/TestObject.php index e8120bddf..3569b189a 100644 --- a/src/Magento/FunctionalTestingFramework/Test/Objects/TestObject.php +++ b/src/Magento/FunctionalTestingFramework/Test/Objects/TestObject.php @@ -15,11 +15,13 @@ */ class TestObject { + const WAIT_TIME_ATTRIBUTE = 'time'; + const TEST_ACTION_WEIGHT = [ 'waitForPageLoad' => 1500, 'amOnPage' => 1000, 'waitForLoadingMaskToDisappear' => 500, - 'wait' => 'time', + 'wait' => self::WAIT_TIME_ATTRIBUTE, 'comment' => 5, 'assertCount' => 5, 'closeAdminNotification' => 10 @@ -208,7 +210,7 @@ private function calculateWeightedActionTimes($actions) /** @var ActionObject $action */ if (array_key_exists($action->getType(), self::TEST_ACTION_WEIGHT)) { $weight = self::TEST_ACTION_WEIGHT[$action->getType()]; - if (is_string($weight) && $weight === 'time') { + if ($weight === self::WAIT_TIME_ATTRIBUTE) { $weight = intval($action->getCustomActionAttributes()[$weight]) * 1000; } diff --git a/src/Magento/FunctionalTestingFramework/Util/Sorter/ParallelGroupSorter.php b/src/Magento/FunctionalTestingFramework/Util/Sorter/ParallelGroupSorter.php index 70bda7c4d..eff9c6d8e 100644 --- a/src/Magento/FunctionalTestingFramework/Util/Sorter/ParallelGroupSorter.php +++ b/src/Magento/FunctionalTestingFramework/Util/Sorter/ParallelGroupSorter.php @@ -237,9 +237,9 @@ private function getSuiteToSize($suiteNamesToTests) private function splitTestSuite($suiteName, $tests, $maxTime) { arsort($tests); - $split_suites = []; + $splitSuites = []; $availableTests = $tests; - $split_count = 0; + $splitCount = 0; foreach ($tests as $test => $size) { if (!array_key_exists($test, $availableTests)) { @@ -247,14 +247,14 @@ private function splitTestSuite($suiteName, $tests, $maxTime) } $group = $this->createTestGroup($maxTime, $test, $size, $availableTests); - $split_suites["{$suiteName}_${split_count}"] = $group; - $this->addSuiteToConfig($suiteName, "{$suiteName}_${split_count}", $group); + $splitSuites["{$suiteName}_${splitCount}"] = $group; + $this->addSuiteToConfig($suiteName, "{$suiteName}_${splitCount}", $group); $availableTests = array_diff_key($availableTests, $group); - $split_count++; + $splitCount++; } - return $split_suites; + return $splitSuites; } /** From 400d5e582bf7374089962b087458c10155349e73 Mon Sep 17 00:00:00 2001 From: Alex Calandra <acalandra@magento.com> Date: Mon, 4 Jun 2018 16:11:23 -0500 Subject: [PATCH 046/111] MQE-1031: Empty files are flagged during generation or dealt with gracefully - Added check for empty file --- .../Config/Reader/FilesystemTest.php | 108 ++++++++++++++++++ .../Config/Reader/Filesystem.php | 26 +++++ .../Config/Reader/MftfFilesystem.php | 4 + 3 files changed, 138 insertions(+) create mode 100644 dev/tests/unit/Magento/FunctionalTestFramework/Config/Reader/FilesystemTest.php diff --git a/dev/tests/unit/Magento/FunctionalTestFramework/Config/Reader/FilesystemTest.php b/dev/tests/unit/Magento/FunctionalTestFramework/Config/Reader/FilesystemTest.php new file mode 100644 index 000000000..b59858e7f --- /dev/null +++ b/dev/tests/unit/Magento/FunctionalTestFramework/Config/Reader/FilesystemTest.php @@ -0,0 +1,108 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Tests\unit\Magento\FunctionalTestFramework\Test\Config\Reader; + +use Magento\FunctionalTestingFramework\Config\FileResolver\Module; +use Magento\FunctionalTestingFramework\Config\Reader\Filesystem; +use Magento\FunctionalTestingFramework\Config\ValidationState; +use Magento\FunctionalTestingFramework\Util\Iterator\File; +use PHPUnit\Framework\TestCase; +use AspectMock\Test as AspectMock; +use tests\unit\Util\TestLoggingUtil; + +class FilesystemTest extends TestCase +{ + /** + * Before test functionality + * @return void + */ + public function setUp() + { + TestLoggingUtil::getInstance()->setMockLoggingUtil(); + } + + /** + * Test Reading Empty Files + * @throws \Exception + */ + public function testEmptyXmlFile() + { + // create mocked items and read the file + $someFile = $this->setMockFile("somepath.xml", ""); + $filesystem = $this->createPseudoFileSystem($someFile); + $filesystem->read(); + + // validate log statement + TestLoggingUtil::getInstance()->validateMockLogStatement( + "warning", + "XML File is empty.", + ["File" => "somepath.xml"] + ); + } + + /** + * Function used to set mock for File created in test + * + * @param string $fileName + * @param string $content + * @return object + * @throws \Exception + */ + public function setMockFile($fileName, $content) + { + $file = AspectMock::double( + File::class, + [ + 'current' => "", + 'count' => 1, + 'getFilename' => $fileName + ] + )->make(); + + //set mocked data property for File + $property = new \ReflectionProperty(File::class, 'data'); + $property->setAccessible(true); + $property->setValue($file, [$fileName => $content]); + + return $file; + } + + /** + * Function used to set mock for filesystem class during test + * + * @param string $fileList + * @return object + * @throws \Exception + */ + public function createPseudoFileSystem($fileList) + { + $filesystem = AspectMock::double(Filesystem::class)->make(); + + //set resolver to use mocked resolver + $mockFileResolver = AspectMock::double(Module::class, ['get' => $fileList])->make(); + $property = new \ReflectionProperty(Filesystem::class, 'fileResolver'); + $property->setAccessible(true); + $property->setValue($filesystem, $mockFileResolver); + + //set validator to use mocked validator + $mockValidation = AspectMock::double(ValidationState::class, ['isValidationRequired' => false])->make(); + $property = new \ReflectionProperty(Filesystem::class, 'validationState'); + $property->setAccessible(true); + $property->setValue($filesystem, $mockValidation); + + return $filesystem; + } + + /** + * After class functionality + * @return void + */ + public static function tearDownAfterClass() + { + TestLoggingUtil::getInstance()->clearMockLoggingUtil(); + parent::tearDownAfterClass(); + } +} diff --git a/src/Magento/FunctionalTestingFramework/Config/Reader/Filesystem.php b/src/Magento/FunctionalTestingFramework/Config/Reader/Filesystem.php index adcb330ba..b8c32b048 100644 --- a/src/Magento/FunctionalTestingFramework/Config/Reader/Filesystem.php +++ b/src/Magento/FunctionalTestingFramework/Config/Reader/Filesystem.php @@ -4,6 +4,8 @@ * See COPYING.txt for license details. */ namespace Magento\FunctionalTestingFramework\Config\Reader; +use Magento\FunctionalTestingFramework\Config\MftfApplicationConfig; +use Magento\FunctionalTestingFramework\Util\Logger\LoggingUtil; /** * Filesystem configuration loader. Loads configuration from XML files, split by scopes. @@ -144,6 +146,10 @@ protected function readFiles($fileList) /** @var \Magento\FunctionalTestingFramework\Config\Dom $configMerger */ $configMerger = null; foreach ($fileList as $key => $content) { + //check if file is empty and continue to next if it is + if (!$this->verifyFileStatus($content, $fileList->getFilename())) { + continue; + } try { if (!$configMerger) { $configMerger = $this->createConfigMerger($this->domDocumentClass, $content); @@ -192,4 +198,24 @@ protected function createConfigMerger($mergerClass, $initialContents) } return $result; } + + /** + * Checks if content is empty and logs warning, returns false if file is empty + * + * @param string $content + * @param string $fileName + * @return bool + */ + protected function verifyFileStatus($content, $fileName) + { + if (empty($content)) { + if (MftfApplicationConfig::getConfig()->verboseEnabled()) { + LoggingUtil::getInstance()->getLogger(Filesystem::class)->warn( + "XML File is empty.", ["File" => $fileName] + ); + } + return false; + } + return true; + } } diff --git a/src/Magento/FunctionalTestingFramework/Config/Reader/MftfFilesystem.php b/src/Magento/FunctionalTestingFramework/Config/Reader/MftfFilesystem.php index e39e39199..9223f3f3f 100644 --- a/src/Magento/FunctionalTestingFramework/Config/Reader/MftfFilesystem.php +++ b/src/Magento/FunctionalTestingFramework/Config/Reader/MftfFilesystem.php @@ -25,6 +25,10 @@ public function readFiles($fileList) /** @var \Magento\FunctionalTestingFramework\Test\Config\Dom $configMerger */ $configMerger = null; foreach ($fileList as $key => $content) { + //check if file is empty and continue to next if it is + if (!parent::verifyFileStatus($content, $fileList->getFilename())) { + continue; + } try { if (!$configMerger) { $configMerger = $this->createConfigMerger( From aff7f6782daeb1bca353fd8cb2e11bdbabe71bc0 Mon Sep 17 00:00:00 2001 From: Alex Calandra <calandra.aj@gmail.com> Date: Tue, 5 Jun 2018 08:44:08 -0500 Subject: [PATCH 047/111] MQE-231: Allow data to extend other data (#136) * MQE-231: Allow data to extend other data - Added simple test and adjusted so data could extend other data * MQE-231: Allow data to extend other data - Added Tests - Updated Extend Login - Cleaned Up Tests using double by adding new TestCase class to extend * MQE-231: Allow data to extend other data - Removed unused references - Removed references to teardown outside of MagentoTestCase * MQE-231: Allow data to extend other data - Missed a clean reference * MQE-231: Allow data to extend other data - Added support to overwrite parent type or take parent type if null * MQE-231: Allow data to extend other data - Fixed comments referring to tests --- .../Handlers/DataObjectHandlerTest.php | 4 +- .../OperationDefinitionObjectHandlerTest.php | 4 +- .../Objects/EntityDataObjectTest.php | 4 +- .../OperationDataArrayResolverTest.php | 4 +- .../Util/DataExtensionUtilTest.php | 155 ++++++++++++++++++ .../Page/Handlers/PageObjectHandlerTest.php | 4 +- .../Handlers/SectionObjectHandlerTest.php | 4 +- .../Page/Objects/ElementObjectTest.php | 4 +- .../Page/Objects/PageObjectTest.php | 4 +- .../Page/Objects/SectionObjectTest.php | 4 +- .../Suite/Handlers/SuiteObjectHandlerTest.php | 4 +- .../Suite/SuiteGeneratorTest.php | 11 +- .../Test/Config/ActionGroupDomTest.php | 4 +- .../Test/Handlers/TestObjectHandlerTest.php | 4 +- .../Test/Objects/ActionGroupObjectTest.php | 4 +- .../Test/Objects/ActionObjectTest.php | 4 +- .../Test/Util/ActionMergeUtilTest.php | 4 +- .../Test/Util/ActionObjectExtractorTest.php | 4 +- .../Util/ModulePathExtractorTest.php | 44 ++++- .../Util/ModuleResolverTest.php | 61 ++++--- .../Util/Sorter/ParallelGroupSorterTest.php | 4 +- .../Validation/NameValidationUtilTest.php | 4 +- dev/tests/unit/Util/MagentoTestCase.php | 24 +++ .../Resources/ExtendParentDataTest.txt | 41 +++++ .../Resources/ExtendedParameterArrayTest.txt | 38 +++++ .../TestModule/Data/ExtendedData.xml | 23 +++ .../TestModule/Test/ExtendedDataTest.xml | 19 +++ .../verification/Tests/ExtendedDataTest.php | 22 +++ .../Tests/SuiteGenerationTest.php | 28 +++- .../Handlers/DataObjectHandler.php | 48 +++++- .../Objects/EntityDataObject.php | 21 ++- .../DataGenerator/Util/DataExtensionUtil.php | 98 +++++++++++ .../DataGenerator/etc/dataProfileSchema.xsd | 7 + 33 files changed, 635 insertions(+), 77 deletions(-) create mode 100644 dev/tests/unit/Magento/FunctionalTestFramework/DataGenerator/Util/DataExtensionUtilTest.php create mode 100644 dev/tests/unit/Util/MagentoTestCase.php create mode 100644 dev/tests/verification/Resources/ExtendParentDataTest.txt create mode 100644 dev/tests/verification/Resources/ExtendedParameterArrayTest.txt create mode 100644 dev/tests/verification/TestModule/Data/ExtendedData.xml create mode 100644 dev/tests/verification/TestModule/Test/ExtendedDataTest.xml create mode 100644 dev/tests/verification/Tests/ExtendedDataTest.php create mode 100644 src/Magento/FunctionalTestingFramework/DataGenerator/Util/DataExtensionUtil.php diff --git a/dev/tests/unit/Magento/FunctionalTestFramework/DataGenerator/Handlers/DataObjectHandlerTest.php b/dev/tests/unit/Magento/FunctionalTestFramework/DataGenerator/Handlers/DataObjectHandlerTest.php index c09e47478..b77659aa2 100644 --- a/dev/tests/unit/Magento/FunctionalTestFramework/DataGenerator/Handlers/DataObjectHandlerTest.php +++ b/dev/tests/unit/Magento/FunctionalTestFramework/DataGenerator/Handlers/DataObjectHandlerTest.php @@ -12,12 +12,12 @@ use Magento\FunctionalTestingFramework\DataGenerator\Parsers\DataProfileSchemaParser; use Magento\FunctionalTestingFramework\ObjectManager; use Magento\FunctionalTestingFramework\ObjectManagerFactory; -use PHPUnit\Framework\TestCase; +use Magento\FunctionalTestingFramework\Util\MagentoTestCase; /** * Class DataObjectHandlerTest */ -class DataObjectHandlerTest extends TestCase +class DataObjectHandlerTest extends MagentoTestCase { // All tests share this array, feel free to add but be careful modifying or removing const PARSER_OUTPUT = [ diff --git a/dev/tests/unit/Magento/FunctionalTestFramework/DataGenerator/Handlers/OperationDefinitionObjectHandlerTest.php b/dev/tests/unit/Magento/FunctionalTestFramework/DataGenerator/Handlers/OperationDefinitionObjectHandlerTest.php index 3050b3830..ffb15648a 100644 --- a/dev/tests/unit/Magento/FunctionalTestFramework/DataGenerator/Handlers/OperationDefinitionObjectHandlerTest.php +++ b/dev/tests/unit/Magento/FunctionalTestFramework/DataGenerator/Handlers/OperationDefinitionObjectHandlerTest.php @@ -13,12 +13,12 @@ use Magento\FunctionalTestingFramework\ObjectManagerFactory; use Magento\FunctionalTestingFramework\DataGenerator\Handlers\OperationDefinitionObjectHandler; use Magento\FunctionalTestingFramework\DataGenerator\Parsers\OperationDefinitionParser; -use PHPUnit\Framework\TestCase; +use Magento\FunctionalTestingFramework\Util\MagentoTestCase; /** * Class OperationDefinitionObjectHandlerTest */ -class OperationDefinitionObjectHandlerTest extends TestCase +class OperationDefinitionObjectHandlerTest extends MagentoTestCase { public function testGetMultipleObjects() { diff --git a/dev/tests/unit/Magento/FunctionalTestFramework/DataGenerator/Objects/EntityDataObjectTest.php b/dev/tests/unit/Magento/FunctionalTestFramework/DataGenerator/Objects/EntityDataObjectTest.php index ab6d52135..0545fe455 100644 --- a/dev/tests/unit/Magento/FunctionalTestFramework/DataGenerator/Objects/EntityDataObjectTest.php +++ b/dev/tests/unit/Magento/FunctionalTestFramework/DataGenerator/Objects/EntityDataObjectTest.php @@ -6,7 +6,7 @@ namespace Magento\FunctionalTestingFramework\DataGenerator\Objects; -use PHPUnit\Framework\TestCase; +use Magento\FunctionalTestingFramework\Util\MagentoTestCase; use tests\unit\Util\TestLoggingUtil; /** @@ -33,7 +33,7 @@ function msqs($id = null) /** * Class EntityDataObjectTest */ -class EntityDataObjectTest extends TestCase +class EntityDataObjectTest extends MagentoTestCase { /** * Before test functionality diff --git a/dev/tests/unit/Magento/FunctionalTestFramework/DataGenerator/Persist/OperationDataArrayResolverTest.php b/dev/tests/unit/Magento/FunctionalTestFramework/DataGenerator/Persist/OperationDataArrayResolverTest.php index ea873c2e9..9e425e020 100644 --- a/dev/tests/unit/Magento/FunctionalTestFramework/DataGenerator/Persist/OperationDataArrayResolverTest.php +++ b/dev/tests/unit/Magento/FunctionalTestFramework/DataGenerator/Persist/OperationDataArrayResolverTest.php @@ -9,13 +9,13 @@ use Magento\FunctionalTestingFramework\DataGenerator\Handlers\DataObjectHandler; use Magento\FunctionalTestingFramework\DataGenerator\Handlers\OperationDefinitionObjectHandler; use Magento\FunctionalTestingFramework\DataGenerator\Persist\OperationDataArrayResolver; -use PHPUnit\Framework\TestCase; +use Magento\FunctionalTestingFramework\Util\MagentoTestCase; use tests\unit\Util\EntityDataObjectBuilder; use tests\unit\Util\OperationDefinitionBuilder; use tests\unit\Util\OperationElementBuilder; use tests\unit\Util\TestLoggingUtil; -class OperationDataArrayResolverTest extends TestCase +class OperationDataArrayResolverTest extends MagentoTestCase { const NESTED_METADATA_EXPECTED_RESULT = ["parentType" => [ "name" => "Hopper", diff --git a/dev/tests/unit/Magento/FunctionalTestFramework/DataGenerator/Util/DataExtensionUtilTest.php b/dev/tests/unit/Magento/FunctionalTestFramework/DataGenerator/Util/DataExtensionUtilTest.php new file mode 100644 index 000000000..e72c15b31 --- /dev/null +++ b/dev/tests/unit/Magento/FunctionalTestFramework/DataGenerator/Util/DataExtensionUtilTest.php @@ -0,0 +1,155 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\FunctionalTestingFramework\DataGenerator\Objects; + +use Magento\FunctionalTestingFramework\DataGenerator\Handlers\DataObjectHandler; +use Magento\FunctionalTestingFramework\DataGenerator\Parsers\DataProfileSchemaParser; +use Magento\FunctionalTestingFramework\ObjectManager\ObjectManager; +use Magento\FunctionalTestingFramework\ObjectManagerFactory; +use Magento\FunctionalTestingFramework\Util\MagentoTestCase; +use AspectMock\Test as AspectMock; + +/** + * Class EntityDataObjectTest + */ +class EntityDataExtensionTest extends MagentoTestCase +{ + /** + * Before method functionality + * @return void + */ + protected function setUp() + { + AspectMock::clean(); + } + + public function testNoParentData() + { + $extendedDataObject = [ + 'entity' => [ + 'extended' => [ + 'type' => 'testType', + 'extends' => "parent", + 'data' => [ + 0 => [ + 'key' => 'testKey', + 'value' => 'testValue' + ] + ] + ] + ] + ]; + + $this->setMockEntities($extendedDataObject); + + $this->expectExceptionMessage("Parent Entity parent not defined for Entity extended."); + DataObjectHandler::getInstance()->getObject("extended"); + } + + public function testAlreadyExtendedParentData() + { + $extendedDataObjects = [ + 'entity' => [ + 'extended' => [ + 'type' => 'testType', + 'extends' => "parent" + ], + 'parent' => [ + 'type' => 'type', + 'extends' => "grandparent" + ], + 'grandparent' => [ + 'type' => 'grand' + ] + ] + ]; + + $this->setMockEntities($extendedDataObjects); + + $this->expectExceptionMessage( + "Cannot extend an entity that already extends another entity. Entity: parent." . PHP_EOL + ); + DataObjectHandler::getInstance()->getObject("extended"); + } + + public function testExtendedVarGetter() + { + $extendedDataObjects = [ + 'entity' => [ + 'extended' => [ + 'type' => 'testType', + 'extends' => "parent" + ], + 'parent' => [ + 'type' => 'type', + 'var' => [ + 'someOtherEntity' => [ + 'entityType' => 'someOtherEntity', + 'entityKey' => 'id', + 'key' => 'someOtherEntity' + ] + ] + ] + ] + ]; + + $this->setMockEntities($extendedDataObjects); + $resultextendedDataObject = DataObjectHandler::getInstance()->getObject("extended"); + // Perform Asserts + $this->assertEquals("someOtherEntity->id", $resultextendedDataObject->getVarReference("someOtherEntity")); + } + + public function testGetLinkedEntities() + { + $extendedDataObjects = [ + 'entity' => [ + 'extended' => [ + 'type' => 'testType', + 'extends' => "parent" + ], + 'parent' => [ + 'type' => 'type', + 'requiredEntity' => [ + 'linkedEntity1' => [ + 'type' => 'linkedEntityType', + 'value' => 'linkedEntity1' + ], + 'linkedEntity2' => [ + 'type' => 'otherEntityType', + 'value' => 'linkedEntity2' + ], + ] + ] + ] + ]; + + $this->setMockEntities($extendedDataObjects); + // Perform Asserts + $resultextendedDataObject = DataObjectHandler::getInstance()->getObject("extended"); + $this->assertEquals("linkedEntity1", $resultextendedDataObject->getLinkedEntitiesOfType("linkedEntityType")[0]); + $this->assertEquals("linkedEntity2", $resultextendedDataObject->getLinkedEntitiesOfType("otherEntityType")[0]); + } + + private function setMockEntities($mockEntityData) + { + $property = new \ReflectionProperty(DataObjectHandler::class, 'INSTANCE'); + $property->setAccessible(true); + $property->setValue(null); + + $mockDataProfileSchemaParser = AspectMock::double(DataProfileSchemaParser::class, [ + 'readDataProfiles' => $mockEntityData + ])->make(); + + $mockObjectManager = AspectMock::double(ObjectManager::class, [ + 'create' => $mockDataProfileSchemaParser + ])->make(); + + AspectMock::double(ObjectManagerFactory::class, [ + 'getObjectManager' => $mockObjectManager + ]); + } +} diff --git a/dev/tests/unit/Magento/FunctionalTestFramework/Page/Handlers/PageObjectHandlerTest.php b/dev/tests/unit/Magento/FunctionalTestFramework/Page/Handlers/PageObjectHandlerTest.php index 62e87022d..28ffcd9e1 100644 --- a/dev/tests/unit/Magento/FunctionalTestFramework/Page/Handlers/PageObjectHandlerTest.php +++ b/dev/tests/unit/Magento/FunctionalTestFramework/Page/Handlers/PageObjectHandlerTest.php @@ -11,9 +11,9 @@ use Magento\FunctionalTestingFramework\ObjectManagerFactory; use Magento\FunctionalTestingFramework\Page\Handlers\PageObjectHandler; use Magento\FunctionalTestingFramework\XmlParser\PageParser; -use PHPUnit\Framework\TestCase; +use Magento\FunctionalTestingFramework\Util\MagentoTestCase; -class PageObjectHandlerTest extends TestCase +class PageObjectHandlerTest extends MagentoTestCase { public function testGetPageObject() { diff --git a/dev/tests/unit/Magento/FunctionalTestFramework/Page/Handlers/SectionObjectHandlerTest.php b/dev/tests/unit/Magento/FunctionalTestFramework/Page/Handlers/SectionObjectHandlerTest.php index 2f79b3680..4a939dbad 100644 --- a/dev/tests/unit/Magento/FunctionalTestFramework/Page/Handlers/SectionObjectHandlerTest.php +++ b/dev/tests/unit/Magento/FunctionalTestFramework/Page/Handlers/SectionObjectHandlerTest.php @@ -11,9 +11,9 @@ use Magento\FunctionalTestingFramework\ObjectManagerFactory; use Magento\FunctionalTestingFramework\Page\Handlers\SectionObjectHandler; use Magento\FunctionalTestingFramework\XmlParser\SectionParser; -use PHPUnit\Framework\TestCase; +use Magento\FunctionalTestingFramework\Util\MagentoTestCase; -class SectionObjectHandlerTest extends TestCase +class SectionObjectHandlerTest extends MagentoTestCase { public function testGetSectionObject() { diff --git a/dev/tests/unit/Magento/FunctionalTestFramework/Page/Objects/ElementObjectTest.php b/dev/tests/unit/Magento/FunctionalTestFramework/Page/Objects/ElementObjectTest.php index 00d5e9479..d585f4085 100644 --- a/dev/tests/unit/Magento/FunctionalTestFramework/Page/Objects/ElementObjectTest.php +++ b/dev/tests/unit/Magento/FunctionalTestFramework/Page/Objects/ElementObjectTest.php @@ -8,12 +8,12 @@ use Magento\FunctionalTestingFramework\Exceptions\XmlException; use Magento\FunctionalTestingFramework\Page\Objects\ElementObject; -use PHPUnit\Framework\TestCase; +use Magento\FunctionalTestingFramework\Util\MagentoTestCase; /** * Class ElementObjectTest */ -class ElementObjectTest extends TestCase +class ElementObjectTest extends MagentoTestCase { /** * Timeout should be null when instantiated with '-' diff --git a/dev/tests/unit/Magento/FunctionalTestFramework/Page/Objects/PageObjectTest.php b/dev/tests/unit/Magento/FunctionalTestFramework/Page/Objects/PageObjectTest.php index 32973217a..7f8053e77 100644 --- a/dev/tests/unit/Magento/FunctionalTestFramework/Page/Objects/PageObjectTest.php +++ b/dev/tests/unit/Magento/FunctionalTestFramework/Page/Objects/PageObjectTest.php @@ -7,12 +7,12 @@ namespace tests\unit\Magento\FunctionalTestFramework\Page\Objects; use Magento\FunctionalTestingFramework\Page\Objects\PageObject; -use PHPUnit\Framework\TestCase; +use Magento\FunctionalTestingFramework\Util\MagentoTestCase; /** * Class PageObjectTest */ -class PageObjectTest extends TestCase +class PageObjectTest extends MagentoTestCase { /** * Assert that the page object has a section diff --git a/dev/tests/unit/Magento/FunctionalTestFramework/Page/Objects/SectionObjectTest.php b/dev/tests/unit/Magento/FunctionalTestFramework/Page/Objects/SectionObjectTest.php index 6257ee0db..5ed1f557f 100644 --- a/dev/tests/unit/Magento/FunctionalTestFramework/Page/Objects/SectionObjectTest.php +++ b/dev/tests/unit/Magento/FunctionalTestFramework/Page/Objects/SectionObjectTest.php @@ -8,12 +8,12 @@ use Magento\FunctionalTestingFramework\Page\Objects\ElementObject; use Magento\FunctionalTestingFramework\Page\Objects\SectionObject; -use PHPUnit\Framework\TestCase; +use Magento\FunctionalTestingFramework\Util\MagentoTestCase; /** * Class SectionObjectTest */ -class SectionObjectTest extends TestCase +class SectionObjectTest extends MagentoTestCase { /** * Assert that the section object has an element diff --git a/dev/tests/unit/Magento/FunctionalTestFramework/Suite/Handlers/SuiteObjectHandlerTest.php b/dev/tests/unit/Magento/FunctionalTestFramework/Suite/Handlers/SuiteObjectHandlerTest.php index ede99bf34..3f0d2c5a8 100644 --- a/dev/tests/unit/Magento/FunctionalTestFramework/Suite/Handlers/SuiteObjectHandlerTest.php +++ b/dev/tests/unit/Magento/FunctionalTestFramework/Suite/Handlers/SuiteObjectHandlerTest.php @@ -12,11 +12,11 @@ use Magento\FunctionalTestingFramework\Suite\Parsers\SuiteDataParser; use Magento\FunctionalTestingFramework\Test\Handlers\TestObjectHandler; use Magento\FunctionalTestingFramework\Test\Parsers\TestDataParser; -use PHPUnit\Framework\TestCase; +use Magento\FunctionalTestingFramework\Util\MagentoTestCase; use tests\unit\Util\SuiteDataArrayBuilder; use tests\unit\Util\TestDataArrayBuilder; -class SuiteObjectHandlerTest extends TestCase +class SuiteObjectHandlerTest extends MagentoTestCase { /** * Tests basic parsing and accesors of suite object and suite object supporting classes diff --git a/dev/tests/unit/Magento/FunctionalTestFramework/Suite/SuiteGeneratorTest.php b/dev/tests/unit/Magento/FunctionalTestFramework/Suite/SuiteGeneratorTest.php index a45ec9f32..7bef700ab 100644 --- a/dev/tests/unit/Magento/FunctionalTestFramework/Suite/SuiteGeneratorTest.php +++ b/dev/tests/unit/Magento/FunctionalTestFramework/Suite/SuiteGeneratorTest.php @@ -16,12 +16,12 @@ use Magento\FunctionalTestingFramework\Test\Util\TestObjectExtractor; use Magento\FunctionalTestingFramework\Test\Parsers\TestDataParser; use Magento\FunctionalTestingFramework\Util\Manifest\DefaultTestManifest; -use PHPUnit\Framework\TestCase; +use Magento\FunctionalTestingFramework\Util\MagentoTestCase; use tests\unit\Util\SuiteDataArrayBuilder; use tests\unit\Util\TestDataArrayBuilder; use tests\unit\Util\TestLoggingUtil; -class SuiteGeneratorTest extends TestCase +class SuiteGeneratorTest extends MagentoTestCase { /** @@ -76,7 +76,7 @@ public function testGenerateSuite() TestLoggingUtil::getInstance()->validateMockLogStatement( 'info', "suite generated", - ['suite' => 'basicTestSuite', 'relative_path' => "_generated/basicTestSuite"] + ['suite' => 'basicTestSuite', 'relative_path' => "_generated" . DIRECTORY_SEPARATOR . "basicTestSuite"] ); } @@ -105,7 +105,7 @@ public function testGenerateAllSuites() $this->setMockTestAndSuiteParserOutput($mockTestData, $mockData); // parse and retrieve suite object with mocked data - $exampleTestManifest = new DefaultTestManifest([], "sample/path"); + $exampleTestManifest = new DefaultTestManifest([], "sample" . DIRECTORY_SEPARATOR . "path"); $mockSuiteGenerator = SuiteGenerator::getInstance(); $mockSuiteGenerator->generateAllSuites($exampleTestManifest); @@ -113,7 +113,7 @@ public function testGenerateAllSuites() TestLoggingUtil::getInstance()->validateMockLogStatement( 'info', "suite generated", - ['suite' => 'basicTestSuite', 'relative_path' => "_generated/basicTestSuite"] + ['suite' => 'basicTestSuite', 'relative_path' => "_generated" . DIRECTORY_SEPARATOR . "basicTestSuite"] ); } @@ -206,5 +206,6 @@ private function setMockTestAndSuiteParserOutput($testData, $suiteData) public static function tearDownAfterClass() { TestLoggingUtil::getInstance()->clearMockLoggingUtil(); + parent::tearDownAfterClass(); } } diff --git a/dev/tests/unit/Magento/FunctionalTestFramework/Test/Config/ActionGroupDomTest.php b/dev/tests/unit/Magento/FunctionalTestFramework/Test/Config/ActionGroupDomTest.php index b7ea66f65..8d0bc19ca 100644 --- a/dev/tests/unit/Magento/FunctionalTestFramework/Test/Config/ActionGroupDomTest.php +++ b/dev/tests/unit/Magento/FunctionalTestFramework/Test/Config/ActionGroupDomTest.php @@ -7,9 +7,9 @@ use Magento\FunctionalTestingFramework\Exceptions\Collector\ExceptionCollector; use Magento\FunctionalTestingFramework\Test\Config\ActionGroupDom; -use PHPUnit\Framework\TestCase; +use Magento\FunctionalTestingFramework\Util\MagentoTestCase; -class ActionGroupDomTest extends TestCase +class ActionGroupDomTest extends MagentoTestCase { /** * Test Action Group duplicate step key validation diff --git a/dev/tests/unit/Magento/FunctionalTestFramework/Test/Handlers/TestObjectHandlerTest.php b/dev/tests/unit/Magento/FunctionalTestFramework/Test/Handlers/TestObjectHandlerTest.php index 54458cd3e..d368d5fa1 100644 --- a/dev/tests/unit/Magento/FunctionalTestFramework/Test/Handlers/TestObjectHandlerTest.php +++ b/dev/tests/unit/Magento/FunctionalTestFramework/Test/Handlers/TestObjectHandlerTest.php @@ -18,10 +18,10 @@ use Magento\FunctionalTestingFramework\Test\Parsers\TestDataParser; use Magento\FunctionalTestingFramework\Test\Util\ActionObjectExtractor; use Magento\FunctionalTestingFramework\Test\Util\TestObjectExtractor; -use PHPUnit\Framework\TestCase; +use Magento\FunctionalTestingFramework\Util\MagentoTestCase; use tests\unit\Util\TestDataArrayBuilder; -class TestObjectHandlerTest extends TestCase +class TestObjectHandlerTest extends MagentoTestCase { /** * Basic test to validate array => test object conversion. diff --git a/dev/tests/unit/Magento/FunctionalTestFramework/Test/Objects/ActionGroupObjectTest.php b/dev/tests/unit/Magento/FunctionalTestFramework/Test/Objects/ActionGroupObjectTest.php index a057ba074..4844ca684 100644 --- a/dev/tests/unit/Magento/FunctionalTestFramework/Test/Objects/ActionGroupObjectTest.php +++ b/dev/tests/unit/Magento/FunctionalTestFramework/Test/Objects/ActionGroupObjectTest.php @@ -15,12 +15,12 @@ use Magento\FunctionalTestingFramework\Test\Objects\ActionGroupObject; use Magento\FunctionalTestingFramework\Test\Objects\ActionObject; use Magento\FunctionalTestingFramework\Test\Objects\ArgumentObject; -use PHPUnit\Framework\TestCase; +use Magento\FunctionalTestingFramework\Util\MagentoTestCase; use tests\unit\Util\ActionGroupObjectBuilder; use tests\unit\Util\EntityDataObjectBuilder; use tests\unit\Util\TestLoggingUtil; -class ActionGroupObjectTest extends TestCase +class ActionGroupObjectTest extends MagentoTestCase { const ACTION_GROUP_MERGE_KEY = 'TestKey'; diff --git a/dev/tests/unit/Magento/FunctionalTestFramework/Test/Objects/ActionObjectTest.php b/dev/tests/unit/Magento/FunctionalTestFramework/Test/Objects/ActionObjectTest.php index 35339eb93..9ad8a01aa 100644 --- a/dev/tests/unit/Magento/FunctionalTestFramework/Test/Objects/ActionObjectTest.php +++ b/dev/tests/unit/Magento/FunctionalTestFramework/Test/Objects/ActionObjectTest.php @@ -15,13 +15,13 @@ use Magento\FunctionalTestingFramework\Test\Objects\ActionObject; use Magento\FunctionalTestingFramework\Page\Handlers\SectionObjectHandler; use Magento\FunctionalTestingFramework\Page\Objects\SectionObject; -use PHPUnit\Framework\TestCase; use tests\unit\Util\TestLoggingUtil; +use Magento\FunctionalTestingFramework\Util\MagentoTestCase; /** * Class ActionObjectTest */ -class ActionObjectTest extends TestCase +class ActionObjectTest extends MagentoTestCase { /** * Before test functionality diff --git a/dev/tests/unit/Magento/FunctionalTestFramework/Test/Util/ActionMergeUtilTest.php b/dev/tests/unit/Magento/FunctionalTestFramework/Test/Util/ActionMergeUtilTest.php index 5bec686da..7a9c6c8c9 100644 --- a/dev/tests/unit/Magento/FunctionalTestFramework/Test/Util/ActionMergeUtilTest.php +++ b/dev/tests/unit/Magento/FunctionalTestFramework/Test/Util/ActionMergeUtilTest.php @@ -11,11 +11,11 @@ use Magento\FunctionalTestingFramework\Test\Objects\ActionObject; use Magento\FunctionalTestingFramework\Test\Util\ActionMergeUtil; use Magento\FunctionalTestingFramework\Test\Util\ActionObjectExtractor; -use PHPUnit\Framework\TestCase; +use Magento\FunctionalTestingFramework\Util\MagentoTestCase; use tests\unit\Util\DataObjectHandlerReflectionUtil; use tests\unit\Util\TestLoggingUtil; -class ActionMergeUtilTest extends TestCase +class ActionMergeUtilTest extends MagentoTestCase { /** * Before test functionality diff --git a/dev/tests/unit/Magento/FunctionalTestFramework/Test/Util/ActionObjectExtractorTest.php b/dev/tests/unit/Magento/FunctionalTestFramework/Test/Util/ActionObjectExtractorTest.php index f52d279eb..7872b3a02 100644 --- a/dev/tests/unit/Magento/FunctionalTestFramework/Test/Util/ActionObjectExtractorTest.php +++ b/dev/tests/unit/Magento/FunctionalTestFramework/Test/Util/ActionObjectExtractorTest.php @@ -7,10 +7,10 @@ use Magento\FunctionalTestingFramework\Test\Objects\ActionObject; use Magento\FunctionalTestingFramework\Test\Util\ActionObjectExtractor; -use PHPUnit\Framework\TestCase; +use Magento\FunctionalTestingFramework\Util\MagentoTestCase; use tests\unit\Util\TestLoggingUtil; -class ActionObjectExtractorTest extends TestCase +class ActionObjectExtractorTest extends MagentoTestCase { /** @var ActionObjectExtractor */ private $testActionObjectExtractor; diff --git a/dev/tests/unit/Magento/FunctionalTestFramework/Util/ModulePathExtractorTest.php b/dev/tests/unit/Magento/FunctionalTestFramework/Util/ModulePathExtractorTest.php index d22d0babf..5efa6384b 100644 --- a/dev/tests/unit/Magento/FunctionalTestFramework/Util/ModulePathExtractorTest.php +++ b/dev/tests/unit/Magento/FunctionalTestFramework/Util/ModulePathExtractorTest.php @@ -11,6 +11,42 @@ class ModulePathExtractorTest extends TestCase { + const EXTENSION_PATH = "app" + . DIRECTORY_SEPARATOR + . "code" + . DIRECTORY_SEPARATOR + . "TestExtension" + . DIRECTORY_SEPARATOR + . "[Analytics]" + . DIRECTORY_SEPARATOR + . "Test" + . DIRECTORY_SEPARATOR + . "Mftf" + . DIRECTORY_SEPARATOR + . "Test" + . DIRECTORY_SEPARATOR + . "SomeText.xml"; + + const MAGENTO_PATH = "dev" + . DIRECTORY_SEPARATOR + . "tests" + . DIRECTORY_SEPARATOR + . "acceptance" + . DIRECTORY_SEPARATOR + . "tests" + . DIRECTORY_SEPARATOR + . "functional" + . DIRECTORY_SEPARATOR + . "Magento" + . DIRECTORY_SEPARATOR + . "FunctionalTest" + . DIRECTORY_SEPARATOR + . "[Analytics]" + . DIRECTORY_SEPARATOR + . "Test" + . DIRECTORY_SEPARATOR + . "SomeText.xml"; + /** * Validate correct module is returned for dev/tests path * @throws \Exception @@ -21,7 +57,7 @@ public function testGetMagentoModule() $this->assertEquals( '[Analytics]', $modulePathExtractor->extractModuleName( - "dev/tests/acceptance/tests/functional/Magento/FunctionalTest/[Analytics]/Test/SomeText.xml" + self::MAGENTO_PATH ) ); } @@ -36,7 +72,7 @@ public function testGetExtensionModule() $this->assertEquals( '[Analytics]', $modulePathExtractor->extractModuleName( - "app/code/Magento/[Analytics]/Test/Mftf/Test/SomeText.xml" + self::EXTENSION_PATH ) ); } @@ -51,7 +87,7 @@ public function testMagentoModulePath() $this->assertEquals( 'Magento', $modulePathExtractor->getExtensionPath( - "dev/tests/acceptance/tests/functional/Magento/FunctionalTest/[Analytics]/Test/SomeText.xml" + self::MAGENTO_PATH ) ); } @@ -66,7 +102,7 @@ public function testExtensionModulePath() $this->assertEquals( 'TestExtension', $modulePathExtractor->getExtensionPath( - "app/code/TestExtension/[Analytics]/Test/Mftf/Test/SomeText.xml" + self::EXTENSION_PATH ) ); } diff --git a/dev/tests/unit/Magento/FunctionalTestFramework/Util/ModuleResolverTest.php b/dev/tests/unit/Magento/FunctionalTestFramework/Util/ModuleResolverTest.php index 0623d47a5..85f09d325 100644 --- a/dev/tests/unit/Magento/FunctionalTestFramework/Util/ModuleResolverTest.php +++ b/dev/tests/unit/Magento/FunctionalTestFramework/Util/ModuleResolverTest.php @@ -13,10 +13,10 @@ use Magento\FunctionalTestingFramework\ObjectManagerFactory; use Magento\FunctionalTestingFramework\Util\Logger\LoggingUtil; use Magento\FunctionalTestingFramework\Util\ModuleResolver; -use PHPUnit\Framework\TestCase; +use Magento\FunctionalTestingFramework\Util\MagentoTestCase; use tests\unit\Util\TestLoggingUtil; -class ModuleResolverTest extends TestCase +class ModuleResolverTest extends MagentoTestCase { /** * Before test functionality @@ -27,14 +27,6 @@ public function setUp() TestLoggingUtil::getInstance()->setMockLoggingUtil(); } - /** - * remove all registered test doubles - */ - protected function tearDown() - { - AspectMock::clean(); - } - /** * After class functionality * @return void @@ -52,8 +44,8 @@ public function testGetModulePathsAlreadySet() { $this->setMockResolverClass(); $resolver = ModuleResolver::getInstance(); - $this->setMockResolverProperties($resolver, ["example/paths"]); - $this->assertEquals(["example/paths"], $resolver->getModulesPath()); + $this->setMockResolverProperties($resolver, ["example" . DIRECTORY_SEPARATOR . "paths"]); + $this->assertEquals(["example" . DIRECTORY_SEPARATOR . "paths"], $resolver->getModulesPath()); } /** @@ -62,11 +54,15 @@ public function testGetModulePathsAlreadySet() */ public function testGetModulePathsAggregate() { - $this->setMockResolverClass(false, null, null, null, ['example/paths']); + $this->setMockResolverClass(false, null, null, null, ["example" . DIRECTORY_SEPARATOR . "paths"]); $resolver = ModuleResolver::getInstance(); $this->setMockResolverProperties($resolver, null, null); $this->assertEquals( - ['example/paths', 'example/paths', 'example/paths'], + [ + "example" . DIRECTORY_SEPARATOR . "paths", + "example" . DIRECTORY_SEPARATOR . "paths", + "example" . DIRECTORY_SEPARATOR . "paths" + ], $resolver->getModulesPath() ); } @@ -77,11 +73,21 @@ public function testGetModulePathsAggregate() */ public function testGetModulePathsLocations() { - $mockResolver = $this->setMockResolverClass(false, null, null, null, ['example/paths']); + $mockResolver = $this->setMockResolverClass( + false, + null, + null, + null, + ["example" . DIRECTORY_SEPARATOR . "paths"] + ); $resolver = ModuleResolver::getInstance(); $this->setMockResolverProperties($resolver, null, null); $this->assertEquals( - ['example/paths', 'example/paths', 'example/paths'], + [ + "example" . DIRECTORY_SEPARATOR . "paths", + "example" . DIRECTORY_SEPARATOR . "paths", + "example" . DIRECTORY_SEPARATOR . "paths" + ], $resolver->getModulesPath() ); @@ -100,8 +106,14 @@ public function testGetModulePathsLocations() . 'vendor' . DIRECTORY_SEPARATOR; $mockResolver->verifyInvoked('globRelevantPaths', [$modulePath, '']); - $mockResolver->verifyInvoked('globRelevantPaths', [$appCodePath, '/Test/Mftf']); - $mockResolver->verifyInvoked('globRelevantPaths', [$vendorCodePath, '/Test/Mftf']); + $mockResolver->verifyInvoked( + 'globRelevantPaths', + [$appCodePath, DIRECTORY_SEPARATOR . 'Test' . DIRECTORY_SEPARATOR .'Mftf'] + ); + $mockResolver->verifyInvoked( + 'globRelevantPaths', + [$vendorCodePath, DIRECTORY_SEPARATOR . 'Test' . DIRECTORY_SEPARATOR .'Mftf'] + ); } /** @@ -159,10 +171,10 @@ function ($arg1, $arg2) { */ public function testGetModulePathsNoAdminToken() { - $this->setMockResolverClass(false, null, ["example/paths"], []); + $this->setMockResolverClass(false, null, ["example" . DIRECTORY_SEPARATOR . "paths"], []); $resolver = ModuleResolver::getInstance(); $this->setMockResolverProperties($resolver, null, null); - $this->assertEquals(["example/paths"], $resolver->getModulesPath()); + $this->assertEquals(["example" . DIRECTORY_SEPARATOR . "paths"], $resolver->getModulesPath()); } /** @@ -247,4 +259,13 @@ private function setMockResolverProperties($instance, $mockPaths = null, $mockMo $property->setAccessible(true); $property->setValue($instance, $mockBlacklist); } + + /** + * After method functionality + * @return void + */ + protected function tearDown() + { + AspectMock::clean(); + } } diff --git a/dev/tests/unit/Magento/FunctionalTestFramework/Util/Sorter/ParallelGroupSorterTest.php b/dev/tests/unit/Magento/FunctionalTestFramework/Util/Sorter/ParallelGroupSorterTest.php index 1acd31522..6c6efe8ea 100644 --- a/dev/tests/unit/Magento/FunctionalTestFramework/Util/Sorter/ParallelGroupSorterTest.php +++ b/dev/tests/unit/Magento/FunctionalTestFramework/Util/Sorter/ParallelGroupSorterTest.php @@ -11,9 +11,9 @@ use Magento\FunctionalTestingFramework\Test\Handlers\TestObjectHandler; use Magento\FunctionalTestingFramework\Test\Objects\TestObject; use Magento\FunctionalTestingFramework\Util\Sorter\ParallelGroupSorter; -use PHPUnit\Framework\TestCase; +use Magento\FunctionalTestingFramework\Util\MagentoTestCase; -class ParallelGroupSorterTest extends TestCase +class ParallelGroupSorterTest extends MagentoTestCase { /** * Test a basic sort of available tests based on size diff --git a/dev/tests/unit/Magento/FunctionalTestFramework/Util/Validation/NameValidationUtilTest.php b/dev/tests/unit/Magento/FunctionalTestFramework/Util/Validation/NameValidationUtilTest.php index 595033012..28d75b1ad 100644 --- a/dev/tests/unit/Magento/FunctionalTestFramework/Util/Validation/NameValidationUtilTest.php +++ b/dev/tests/unit/Magento/FunctionalTestFramework/Util/Validation/NameValidationUtilTest.php @@ -8,9 +8,9 @@ use Magento\FunctionalTestingFramework\Exceptions\XmlException; use Magento\FunctionalTestingFramework\Util\Validation\NameValidationUtil; -use PHPUnit\Framework\TestCase; +use Magento\FunctionalTestingFramework\Util\MagentoTestCase; -class NameValidationUtilTest extends TestCase +class NameValidationUtilTest extends MagentoTestCase { /** * Validate name with curly braces throws exception diff --git a/dev/tests/unit/Util/MagentoTestCase.php b/dev/tests/unit/Util/MagentoTestCase.php new file mode 100644 index 000000000..eafb5ee31 --- /dev/null +++ b/dev/tests/unit/Util/MagentoTestCase.php @@ -0,0 +1,24 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\FunctionalTestingFramework\Util; + +use AspectMock\Test as AspectMock; +use PHPUnit\Framework\TestCase; + +/** + * Class MagentoTestCase + */ +class MagentoTestCase extends TestCase +{ + /** + * Teardown for removing AspectMock Double References + */ + public static function tearDownAfterClass() + { + AspectMock::clean(); + } +} diff --git a/dev/tests/verification/Resources/ExtendParentDataTest.txt b/dev/tests/verification/Resources/ExtendParentDataTest.txt new file mode 100644 index 000000000..5553b2e7a --- /dev/null +++ b/dev/tests/verification/Resources/ExtendParentDataTest.txt @@ -0,0 +1,41 @@ +<?php +namespace Magento\AcceptanceTest\_default\Backend; + +use Magento\FunctionalTestingFramework\AcceptanceTester; +use Magento\FunctionalTestingFramework\DataGenerator\Handlers\DataObjectHandler; +use Magento\FunctionalTestingFramework\DataGenerator\Persist\DataPersistenceHandler; +use Magento\FunctionalTestingFramework\DataGenerator\Objects\EntityDataObject; +use \Codeception\Util\Locator; +use Yandex\Allure\Adapter\Annotation\Features; +use Yandex\Allure\Adapter\Annotation\Stories; +use Yandex\Allure\Adapter\Annotation\Title; +use Yandex\Allure\Adapter\Annotation\Description; +use Yandex\Allure\Adapter\Annotation\Parameter; +use Yandex\Allure\Adapter\Annotation\Severity; +use Yandex\Allure\Adapter\Model\SeverityLevel; +use Yandex\Allure\Adapter\Annotation\TestCaseId; + +/** + */ +class ExtendParentDataTestCest +{ + /** + * @Features({"TestModule"}) + * @Parameter(name = "AcceptanceTester", value="$I") + * @param AcceptanceTester $I + * @return void + * @throws \Exception + */ + public function ExtendParentDataTest(AcceptanceTester $I) + { + $I->amGoingTo("create entity that has the stepKey: simpleDataKey"); + $extendParentData = DataObjectHandler::getInstance()->getObject("extendParentData"); + $simpleDataKey = new DataPersistenceHandler($extendParentData, []); + $simpleDataKey->createEntity(); + $I->searchAndMultiSelectOption("#selector", ["otherName"]); + $I->searchAndMultiSelectOption("#selector", ["extendName"]); + $I->searchAndMultiSelectOption("#selector", ["item"]); + $I->searchAndMultiSelectOption("#selector", [msq("extendParentData") . "prename"]); + $I->searchAndMultiSelectOption("#selector", ["postnameExtend" . msq("extendParentData")]); + } +} diff --git a/dev/tests/verification/Resources/ExtendedParameterArrayTest.txt b/dev/tests/verification/Resources/ExtendedParameterArrayTest.txt new file mode 100644 index 000000000..3c5e284eb --- /dev/null +++ b/dev/tests/verification/Resources/ExtendedParameterArrayTest.txt @@ -0,0 +1,38 @@ +<?php +namespace Magento\AcceptanceTest\_default\Backend; + +use Magento\FunctionalTestingFramework\AcceptanceTester; +use Magento\FunctionalTestingFramework\DataGenerator\Handlers\DataObjectHandler; +use Magento\FunctionalTestingFramework\DataGenerator\Persist\DataPersistenceHandler; +use Magento\FunctionalTestingFramework\DataGenerator\Objects\EntityDataObject; +use \Codeception\Util\Locator; +use Yandex\Allure\Adapter\Annotation\Features; +use Yandex\Allure\Adapter\Annotation\Stories; +use Yandex\Allure\Adapter\Annotation\Title; +use Yandex\Allure\Adapter\Annotation\Description; +use Yandex\Allure\Adapter\Annotation\Parameter; +use Yandex\Allure\Adapter\Annotation\Severity; +use Yandex\Allure\Adapter\Model\SeverityLevel; +use Yandex\Allure\Adapter\Annotation\TestCaseId; + +/** + */ +class ExtendedParameterArrayTestCest +{ + /** + * @Features({"TestModule"}) + * @Parameter(name = "AcceptanceTester", value="$I") + * @param AcceptanceTester $I + * @return void + * @throws \Exception + */ + public function ExtendedParameterArrayTest(AcceptanceTester $I) + { + $I->amGoingTo("create entity that has the stepKey: simpleDataKey"); + $testExtendSimpleParamData = DataObjectHandler::getInstance()->getObject("testExtendSimpleParamData"); + $simpleDataKey = new DataPersistenceHandler($testExtendSimpleParamData, []); + $simpleDataKey->createEntity(); + $I->searchAndMultiSelectOption("#selector", ["otherName"]); + $I->searchAndMultiSelectOption("#selector", ["extendName"]); + } +} diff --git a/dev/tests/verification/TestModule/Data/ExtendedData.xml b/dev/tests/verification/TestModule/Data/ExtendedData.xml new file mode 100644 index 000000000..74fa921d0 --- /dev/null +++ b/dev/tests/verification/TestModule/Data/ExtendedData.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<entities xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="../../../../../src/Magento/FunctionalTestingFramework/DataGenerator/etc/dataProfileSchema.xsd"> + <entity name="parentData" type="data"> + <data key="name">name</data> + <data key="uniqueNamePre" unique="prefix">prename</data> + <data key="uniqueNamePost" unique="suffix">postname</data> + </entity> + <entity name="extendParentData" extends="parentData"> + <data key="name">otherName</data> + <data key="nameExtend">extendName</data> + <data key="uniqueNamePost">item</data> + <data key="anotherUniqueNamePre" unique="suffix">postnameExtend</data> + <requiredEntity type="item">value</requiredEntity> + </entity> +</entities> diff --git a/dev/tests/verification/TestModule/Test/ExtendedDataTest.xml b/dev/tests/verification/TestModule/Test/ExtendedDataTest.xml new file mode 100644 index 000000000..21f06eb3a --- /dev/null +++ b/dev/tests/verification/TestModule/Test/ExtendedDataTest.xml @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="../../../../../src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + <test name="ExtendParentDataTest"> + <createData entity="extendParentData" stepKey="simpleDataKey"/> + <searchAndMultiSelectOption selector="#selector" parameterArray="[{{extendParentData.name}}]" stepKey="getName"/> + <searchAndMultiSelectOption selector="#selector" parameterArray="[{{extendParentData.nameExtend}}]" stepKey="getNameExtend"/> + <searchAndMultiSelectOption selector="#selector" parameterArray="[{{extendParentData.uniqueNamePost}}]" stepKey="emptyPost"/> + <searchAndMultiSelectOption selector="#selector" parameterArray="[{{extendParentData.uniqueNamePre}}]" stepKey="originalPre"/> + <searchAndMultiSelectOption selector="#selector" parameterArray="[{{extendParentData.anotherUniqueNamePre}}]" stepKey="secondUniquePre"/> + </test> +</tests> diff --git a/dev/tests/verification/Tests/ExtendedDataTest.php b/dev/tests/verification/Tests/ExtendedDataTest.php new file mode 100644 index 000000000..291b6cb7f --- /dev/null +++ b/dev/tests/verification/Tests/ExtendedDataTest.php @@ -0,0 +1,22 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +namespace tests\verification\Tests; + +use tests\util\MftfTestCase; + +class ExtendedDataTest extends MftfTestCase +{ + /** + * Tests flat generation of a hardcoded test file with no external references. + * + * @throws \Exception + * @throws \Magento\FunctionalTestingFramework\Exceptions\TestReferenceException + */ + public function testExtendedParameterArrayGeneration() + { + $this->generateAndCompareTest('ExtendParentDataTest'); + } +} diff --git a/dev/tests/verification/Tests/SuiteGenerationTest.php b/dev/tests/verification/Tests/SuiteGenerationTest.php index 0dd7017c0..52b34b3ef 100644 --- a/dev/tests/verification/Tests/SuiteGenerationTest.php +++ b/dev/tests/verification/Tests/SuiteGenerationTest.php @@ -50,7 +50,15 @@ public function setUp() // copy config yml file to test dir $fileSystem = new \Symfony\Component\Filesystem\Filesystem(); $fileSystem->copy( - realpath(FW_BP . '/etc/config/codeception.dist.yml'), + realpath( + FW_BP + . DIRECTORY_SEPARATOR + . 'etc' + . DIRECTORY_SEPARATOR + . 'config' + . DIRECTORY_SEPARATOR + . 'codeception.dist.yml' + ), self::CONFIG_YML_FILE ); @@ -78,7 +86,7 @@ public function testSuiteGeneration1() TestLoggingUtil::getInstance()->validateMockLogStatement( 'info', "suite generated", - ['suite' => $groupName, 'relative_path' => "_generated/$groupName"] + ['suite' => $groupName, 'relative_path' => "_generated" . DIRECTORY_SEPARATOR . $groupName] ); self::$TEST_GROUPS[] = $groupName; @@ -134,7 +142,7 @@ public function testSuiteGenerationParallel() TestLoggingUtil::getInstance()->validateMockLogStatement( 'info', "suite generated", - ['suite' => $expectedGroup, 'relative_path' => "_generated/$expectedGroup"] + ['suite' => $expectedGroup, 'relative_path' => "_generated" . DIRECTORY_SEPARATOR . $expectedGroup] ); self::$TEST_GROUPS[] = $groupName; @@ -176,7 +184,7 @@ public function testSuiteGenerationHooks() TestLoggingUtil::getInstance()->validateMockLogStatement( 'info', "suite generated", - ['suite' => $groupName, 'relative_path' => "_generated/$groupName"] + ['suite' => $groupName, 'relative_path' => "_generated" . DIRECTORY_SEPARATOR . $groupName] ); self::$TEST_GROUPS[] = $groupName; @@ -244,7 +252,7 @@ public function testSuiteGenerationSingleRun() TestLoggingUtil::getInstance()->validateMockLogStatement( 'info', "suite generated", - ['suite' => $groupName, 'relative_path' => "_generated/$groupName"] + ['suite' => $groupName, 'relative_path' => "_generated" . DIRECTORY_SEPARATOR . $groupName] ); self::$TEST_GROUPS[] = $groupName; @@ -264,7 +272,15 @@ public function testSuiteGenerationSingleRun() $this->assertTrue(in_array($expectedFile, $dirContents)); } - $expectedManifest = "verification/_generated/default/" . PHP_EOL . "-g functionalSuite2" . PHP_EOL; + $expectedManifest = "verification" + . DIRECTORY_SEPARATOR + . "_generated" + . DIRECTORY_SEPARATOR + . "default" + . DIRECTORY_SEPARATOR + . PHP_EOL + . "-g functionalSuite2" + . PHP_EOL; $this->assertEquals($expectedManifest, file_get_contents(self::getManifestFilePath())); } diff --git a/src/Magento/FunctionalTestingFramework/DataGenerator/Handlers/DataObjectHandler.php b/src/Magento/FunctionalTestingFramework/DataGenerator/Handlers/DataObjectHandler.php index 0e79bcac1..5dc9293c2 100644 --- a/src/Magento/FunctionalTestingFramework/DataGenerator/Handlers/DataObjectHandler.php +++ b/src/Magento/FunctionalTestingFramework/DataGenerator/Handlers/DataObjectHandler.php @@ -11,12 +11,14 @@ use Magento\FunctionalTestingFramework\Exceptions\XmlException; use Magento\FunctionalTestingFramework\ObjectManager\ObjectHandlerInterface; use Magento\FunctionalTestingFramework\ObjectManagerFactory; +use Magento\FunctionalTestingFramework\DataGenerator\Util\DataExtensionUtil; class DataObjectHandler implements ObjectHandlerInterface { const _ENTITY = 'entity'; const _NAME = 'name'; const _TYPE = 'type'; + const _EXTENDS = 'extends'; const _DATA = 'data'; const _KEY = 'key'; const _VALUE = 'value'; @@ -46,6 +48,13 @@ class DataObjectHandler implements ObjectHandlerInterface */ private $entityDataObjects = []; + /** + * Instance of DataExtensionUtil class + * + * @var DataExtensionUtil + */ + private $extendUtil; + /** * Constructor */ @@ -57,6 +66,7 @@ private function __construct() return; } $this->entityDataObjects = $this->processParserOutput($parserOutput); + $this->extendUtil = new DataExtensionUtil(); } /** @@ -81,10 +91,8 @@ public static function getInstance() */ public function getObject($name) { - $allObjects = $this->getAllObjects(); - - if (array_key_exists($name, $allObjects)) { - return $allObjects[$name]; + if (array_key_exists($name, $this->entityDataObjects)) { + return $this->extendDataObject($this->entityDataObjects[$name]); } return null; @@ -97,6 +105,9 @@ public function getObject($name) */ public function getAllObjects() { + foreach ($this->entityDataObjects as $entityName => $entityObject) { + $this->entityDataObjects[$entityName] = $this->extendDataObject($entityObject); + } return $this->entityDataObjects; } @@ -122,6 +133,7 @@ private function processParserOutput($parserOutput) $linkedEntities = []; $uniquenessData = []; $vars = []; + $parentEntity = null; if (array_key_exists(self::_DATA, $rawEntity)) { $data = $this->processDataElements($rawEntity); @@ -144,7 +156,19 @@ private function processParserOutput($parserOutput) $vars = $this->processVarElements($rawEntity); } - $entityDataObject = new EntityDataObject($name, $type, $data, $linkedEntities, $uniquenessData, $vars); + if (array_key_exists(self::_EXTENDS, $rawEntity)) { + $parentEntity = $rawEntity[self::_EXTENDS]; + } + + $entityDataObject = new EntityDataObject( + $name, + $type, + $data, + $linkedEntities, + $uniquenessData, + $vars, + $parentEntity + ); $entityDataObjects[$entityDataObject->getName()] = $entityDataObject; } @@ -239,4 +263,18 @@ private function processVarElements($entityData) } return $vars; } + + /** + * This method checks if the data object is extended and creates a new data object accordingly + * + * @param EntityDataObject $dataObject + * @return EntityDataObject + */ + private function extendDataObject($dataObject) + { + if ($dataObject->getParentName() != null) { + return $this->extendUtil->extendEntity($dataObject); + } + return $dataObject; + } } diff --git a/src/Magento/FunctionalTestingFramework/DataGenerator/Objects/EntityDataObject.php b/src/Magento/FunctionalTestingFramework/DataGenerator/Objects/EntityDataObject.php index 74e232145..736f76e01 100644 --- a/src/Magento/FunctionalTestingFramework/DataGenerator/Objects/EntityDataObject.php +++ b/src/Magento/FunctionalTestingFramework/DataGenerator/Objects/EntityDataObject.php @@ -65,6 +65,13 @@ class EntityDataObject */ private $uniquenessData = []; + /** + * String of parent Entity + * + * @var string + */ + private $parentEntity; + /** * Constructor * @@ -74,8 +81,9 @@ class EntityDataObject * @param string[] $linkedEntities * @param string[] $uniquenessData * @param string[] $vars + * @param string $parentEntity */ - public function __construct($name, $type, $data, $linkedEntities, $uniquenessData, $vars = []) + public function __construct($name, $type, $data, $linkedEntities, $uniquenessData, $vars = [], $parentEntity = null) { $this->name = $name; $this->type = $type; @@ -86,6 +94,7 @@ public function __construct($name, $type, $data, $linkedEntities, $uniquenessDat } $this->vars = $vars; + $this->parentEntity = $parentEntity; } /** @@ -153,6 +162,16 @@ public function getDataByName($name, $uniquenessFormat) return null; } + /** + * Getter for data parent + * + * @return \string + */ + public function getParentName() + { + return $this->parentEntity; + } + /** * Formats and returns data based on given uniqueDataFormat and prefix/suffix. * diff --git a/src/Magento/FunctionalTestingFramework/DataGenerator/Util/DataExtensionUtil.php b/src/Magento/FunctionalTestingFramework/DataGenerator/Util/DataExtensionUtil.php new file mode 100644 index 000000000..53f0a6e6c --- /dev/null +++ b/src/Magento/FunctionalTestingFramework/DataGenerator/Util/DataExtensionUtil.php @@ -0,0 +1,98 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\FunctionalTestingFramework\DataGenerator\Util; + +use Magento\FunctionalTestingFramework\Config\MftfApplicationConfig; +use Magento\FunctionalTestingFramework\DataGenerator\Handlers\DataObjectHandler; +use Magento\FunctionalTestingFramework\DataGenerator\Objects\EntityDataObject; +use Magento\FunctionalTestingFramework\Exceptions\XmlException; + +class DataExtensionUtil +{ + /** + * ObjectExtensionUtil constructor. + */ + public function __construct() + { + // empty + } + + /** + * Resolves test references for extending test objects + * + * @param EntityDataObject $entityObject + * @return EntityDataObject + * @throws XmlException + */ + public function extendEntity($entityObject) + { + // Check to see if the parent entity exists + $parentEntity = DataObjectHandler::getInstance()->getObject($entityObject->getParentName()); + if ($parentEntity == null) { + throw new XmlException( + "Parent Entity " . + $entityObject->getParentName() . + " not defined for Entity " . + $entityObject->getName() . + "." . + PHP_EOL + ); + } + + // Check to see if the parent entity is already an extended entity + if ($parentEntity->getParentName() !== null) { + throw new XmlException( + "Cannot extend an entity that already extends another entity. Entity: " . + $parentEntity->getName() . + "." . + PHP_EOL + ); + } + if (MftfApplicationConfig::getConfig()->verboseEnabled()) { + echo("Extending Data: " . $parentEntity->getName() . " => " . $entityObject->getName() . PHP_EOL); + } + + //get parent entity type if child does not have a type + $newType = $entityObject->getType() ?? $parentEntity->getType(); + + // Get all data for both parent and child and merge + $referencedData = $parentEntity->getAllData(); + $newData = array_merge($referencedData, $entityObject->getAllData()); + + // Get all linked references for both parent and child and merge + $referencedLinks = $parentEntity->getLinkedEntities(); + $newLinkedReferences = array_merge($referencedLinks, $entityObject->getLinkedEntities()); + + // Get all unique references for both parent and child and merge + $referencedUniqueData = $parentEntity->getUniquenessData(); + $newUniqueReferences = array_merge($referencedUniqueData, $entityObject->getUniquenessData()); + + // Get all var references for both parent and child and merge + $referencedVars = $parentEntity->getVarReferences(); + $newVarReferences = array_merge($referencedVars, $entityObject->getVarReferences()); + + // Remove unique references for objects that are replaced without such reference + $unmatchedUniqueReferences = array_diff_key($referencedUniqueData, $entityObject->getUniquenessData()); + foreach ($unmatchedUniqueReferences as $uniqueKey => $uniqueData) { + if (array_key_exists($uniqueKey, $entityObject->getAllData())) { + unset($newUniqueReferences[$uniqueKey]); + } + } + + // Create new entity object to return + $extendedEntity = new EntityDataObject( + $entityObject->getName(), + $newType, + $newData, + $newLinkedReferences, + $newUniqueReferences, + $newVarReferences, + $entityObject->getParentName() + ); + return $extendedEntity; + } +} diff --git a/src/Magento/FunctionalTestingFramework/DataGenerator/etc/dataProfileSchema.xsd b/src/Magento/FunctionalTestingFramework/DataGenerator/etc/dataProfileSchema.xsd index b956e718b..dbb58ed7f 100644 --- a/src/Magento/FunctionalTestingFramework/DataGenerator/etc/dataProfileSchema.xsd +++ b/src/Magento/FunctionalTestingFramework/DataGenerator/etc/dataProfileSchema.xsd @@ -77,6 +77,13 @@ </xs:documentation> </xs:annotation> </xs:attribute> + <xs:attribute type="xs:string" name="extends"> + <xs:annotation> + <xs:documentation> + Name of the entity that is extended. + </xs:documentation> + </xs:annotation> + </xs:attribute> </xs:complexType> <xs:complexType name="dataType"> From 03f0a65453625cb5483fefbc9d0e53af695f3d2e Mon Sep 17 00:00:00 2001 From: Alex Calandra <acalandra@magento.com> Date: Tue, 5 Jun 2018 12:00:06 -0500 Subject: [PATCH 048/111] MQE-1031: Empty files are flagged during generation or dealt with gracefully - Added windows compatibility for checks - Fixed static check in Filesystem --- .gitignore | 3 +- bin/all-checks.bat | 8 ++++ bin/copyright-check.bat | 41 +++++++++++++++++++ bin/static-checks.bat | 14 +++---- .../Config/Reader/Filesystem.php | 4 +- 5 files changed, 60 insertions(+), 10 deletions(-) create mode 100644 bin/all-checks.bat create mode 100644 bin/copyright-check.bat diff --git a/.gitignore b/.gitignore index da2119a16..c1adae14d 100755 --- a/.gitignore +++ b/.gitignore @@ -13,4 +13,5 @@ coverage/ .vscode codeception.yml dev/tests/functional/MFTF.suite.yml -dev/tests/functional/_output \ No newline at end of file +dev/tests/functional/_output +dev/tests/mftf.log \ No newline at end of file diff --git a/bin/all-checks.bat b/bin/all-checks.bat new file mode 100644 index 000000000..d910923ce --- /dev/null +++ b/bin/all-checks.bat @@ -0,0 +1,8 @@ +:: Copyright © Magento, Inc. All rights reserved. +:: See COPYING.txt for license details. + +@echo off +call bin\static-checks.bat + +@echo off +call bin\phpunit-checks.bat diff --git a/bin/copyright-check.bat b/bin/copyright-check.bat new file mode 100644 index 000000000..9f2e23bfb --- /dev/null +++ b/bin/copyright-check.bat @@ -0,0 +1,41 @@ +:: Copyright © Magento, Inc. All rights reserved. +:: See COPYING.txt for license details. + +@echo off +SETLOCAL EnableDelayedExpansion +SET BLACKLIST_FILE=bin/blacklist.txt +SET i=0 + +FOR /F %%x IN ('git ls-tree --full-tree -r --name-only HEAD') DO ( + SET GOOD_EXT= + if "%%~xx"==".php" set GOOD_EXT=1 + if "%%~xx"==".xml" set GOOD_EXT=1 + if "%%~xx"==".xsd" set GOOD_EXT=1 + IF DEFINED GOOD_EXT ( + SET BLACKLISTED= + FOR /F "tokens=* skip=5" %%f IN (%BLACKLIST_FILE%) DO ( + SET LINE=%%x + IF NOT "!LINE!"=="!LINE:%%f=!" ( + SET BLACKLISTED=1 + ) + ) + IF NOT DEFINED BLACKLISTED ( + FIND "Copyright © Magento, Inc. All rights reserved." %%x >nul + IF ERRORLEVEL 1 ( + SET /A i+=1 + SET NO_COPYRIGHT_LIST[!i!]=%%x + ) + ) + ) +) + +IF DEFINED NO_COPYRIGHT_LIST[1] ( + ECHO THE FOLLOWING FILES ARE MISSING THE MAGENTO COPYRIGHT: + ECHO. + ECHO Copyright © Magento, Inc. All rights reserved. + ECHO See COPYING.txt for license details. + ECHO. + FOR /L %%a IN (1,1,%i%) DO ( + ECHO !NO_COPYRIGHT_LIST[%%a]! + ) +) \ No newline at end of file diff --git a/bin/static-checks.bat b/bin/static-checks.bat index 0b9402094..5e14dd289 100644 --- a/bin/static-checks.bat +++ b/bin/static-checks.bat @@ -5,17 +5,15 @@ @echo off @echo ===============================PHP CODE SNIFFER REPORT=============================== -call vendor\bin\phpcs .\src --standard=.\dev\tests\static\Magento --ignore=src\Magento\FunctionalTestingFramework\Group,src\Magento\FunctionalTestingFramework\AcceptanceTester.php -call vendor\bin\phpcs .\dev\tests\unit --standard=.\dev\tests\static\Magento -call vendor\bin\phpcs .\dev\tests\verification --standard=.\dev\tests\static\Magento --ignore=dev\tests\verification\_generated +call vendor\bin\phpcs --standard=.\dev\tests\static\Magento --ignore=src/Magento/FunctionalTestingFramework/Group,src/Magento/FunctionalTestingFramework/AcceptanceTester.php .\src +call vendor\bin\phpcs --standard=.\dev\tests\static\Magento .\dev\tests\unit +call vendor\bin\phpcs --standard=.\dev\tests\static\Magento --ignore=dev/tests/verification/_generated .\dev\tests\verification @echo ===============================COPY PASTE DETECTOR REPORT=============================== call vendor\bin\phpcpd .\src -@echo "===============================PHP MESS DETECTOR REPORT=============================== -vendor\bin\phpmd .\src text \dev\tests\static\Magento\CodeMessDetector\ruleset.xml --exclude _generated,src\Magento\FunctionalTestingFramework\Group,src\Magento\FunctionalTestingFramework\AcceptanceTester.php +@echo ===============================PHP MESS DETECTOR REPORT=============================== +call vendor\bin\phpmd --exclude _generated,src\Magento\FunctionalTestingFramework\Group,src\Magento\FunctionalTestingFramework\AcceptanceTester.php .\src text \dev\tests\static\Magento\CodeMessDetector\ruleset.xml @echo ===============================MAGENTO COPYRIGHT REPORT=============================== -echo msgbox "INFO:Copyright check currently not run as part of .bat implementation" > "%temp%\popup.vbs" -wscript.exe "%temp%\popup.vbs" -::bin\copyright-check +call bin\copyright-check.bat diff --git a/src/Magento/FunctionalTestingFramework/Config/Reader/Filesystem.php b/src/Magento/FunctionalTestingFramework/Config/Reader/Filesystem.php index b8c32b048..0b4f82546 100644 --- a/src/Magento/FunctionalTestingFramework/Config/Reader/Filesystem.php +++ b/src/Magento/FunctionalTestingFramework/Config/Reader/Filesystem.php @@ -4,6 +4,7 @@ * See COPYING.txt for license details. */ namespace Magento\FunctionalTestingFramework\Config\Reader; + use Magento\FunctionalTestingFramework\Config\MftfApplicationConfig; use Magento\FunctionalTestingFramework\Util\Logger\LoggingUtil; @@ -211,7 +212,8 @@ protected function verifyFileStatus($content, $fileName) if (empty($content)) { if (MftfApplicationConfig::getConfig()->verboseEnabled()) { LoggingUtil::getInstance()->getLogger(Filesystem::class)->warn( - "XML File is empty.", ["File" => $fileName] + "XML File is empty.", + ["File" => $fileName] ); } return false; From 343246badaf2c4a5b7eef52e8536bf32f73e7167 Mon Sep 17 00:00:00 2001 From: Ian Meron <imeron@magento.com> Date: Tue, 5 Jun 2018 14:51:46 -0500 Subject: [PATCH 049/111] MQE-1024: Refactor Algorithm For Mftf Build Groups - add new validation around time parameter (non-negative and non-zero values) --- .../Console/GenerateTestsCommand.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/Magento/FunctionalTestingFramework/Console/GenerateTestsCommand.php b/src/Magento/FunctionalTestingFramework/Console/GenerateTestsCommand.php index 7d8cffc35..119fe48f9 100644 --- a/src/Magento/FunctionalTestingFramework/Console/GenerateTestsCommand.php +++ b/src/Magento/FunctionalTestingFramework/Console/GenerateTestsCommand.php @@ -61,6 +61,11 @@ protected function execute(InputInterface $input, OutputInterface $output) throw new TestFrameworkException("JSON could not be parsed: " . json_last_error_msg()); } + if ($config === 'parallel' && $time <= 0) { + // stop execution if the user has given us an invalid argument for time argument during parallel generation + throw new TestFrameworkException("time option cannot be less than or equal to 0"); + } + $testConfiguration = $this->createTestConfiguration($json, $tests, $force, $verbose); // create our manifest file here From 596efbe770357fa764a47b33ebd695e9c01bda61 Mon Sep 17 00:00:00 2001 From: Alex Calandra <acalandra@magento.com> Date: Wed, 6 Jun 2018 16:14:57 -0500 Subject: [PATCH 050/111] MQE-1017: Better Error Messaging When Non-Whitespace Characters Are Outside XML Elements - Adding debug flag to generate tests - Adding individual file validation for schema when debug is on --- dev/tests/_bootstrap.php | 1 + .../Config/MftfApplicationConfig.php | 34 ++++++++++++++++--- .../Config/Reader/Filesystem.php | 7 ++++ .../Config/Reader/MftfFilesystem.php | 11 +++++- .../Console/GenerateTestsCommand.php | 12 ++++--- 5 files changed, 56 insertions(+), 9 deletions(-) diff --git a/dev/tests/_bootstrap.php b/dev/tests/_bootstrap.php index 0baf41c56..84cf32264 100644 --- a/dev/tests/_bootstrap.php +++ b/dev/tests/_bootstrap.php @@ -31,6 +31,7 @@ \Magento\FunctionalTestingFramework\Config\MftfApplicationConfig::create( true, \Magento\FunctionalTestingFramework\Config\MftfApplicationConfig::GENERATION_PHASE, + true, true ); diff --git a/src/Magento/FunctionalTestingFramework/Config/MftfApplicationConfig.php b/src/Magento/FunctionalTestingFramework/Config/MftfApplicationConfig.php index 4d4500b1e..81505067f 100644 --- a/src/Magento/FunctionalTestingFramework/Config/MftfApplicationConfig.php +++ b/src/Magento/FunctionalTestingFramework/Config/MftfApplicationConfig.php @@ -34,6 +34,13 @@ class MftfApplicationConfig */ private $verboseEnabled; + /** + * Determines whether the user would like to execute mftf in a verbose run. + * + * @var bool + */ + private $debugEnabled; + /** * MftfApplicationConfig Singelton Instance * @@ -47,10 +54,15 @@ class MftfApplicationConfig * @param bool $forceGenerate * @param string $phase * @param bool $verboseEnabled + * @param bool $debugEnabled * @throws TestFrameworkException */ - private function __construct($forceGenerate = false, $phase = self::EXECUTION_PHASE, $verboseEnabled = null) - { + private function __construct( + $forceGenerate = false, + $phase = self::EXECUTION_PHASE, + $verboseEnabled = null, + $debugEnabled = null + ) { $this->forceGenerate = $forceGenerate; if (!in_array($phase, self::MFTF_PHASES)) { @@ -59,6 +71,7 @@ private function __construct($forceGenerate = false, $phase = self::EXECUTION_PH $this->phase = $phase; $this->verboseEnabled = $verboseEnabled; + $this->debugEnabled = $debugEnabled; } /** @@ -68,12 +81,14 @@ private function __construct($forceGenerate = false, $phase = self::EXECUTION_PH * @param bool $forceGenerate * @param string $phase * @param bool $verboseEnabled + * @param bool $debugEnabled * @return void */ - public static function create($forceGenerate, $phase, $verboseEnabled) + public static function create($forceGenerate, $phase, $verboseEnabled, $debugEnabled) { if (self::$MFTF_APPLICATION_CONTEXT == null) { - self::$MFTF_APPLICATION_CONTEXT = new MftfApplicationConfig($forceGenerate, $phase, $verboseEnabled); + self::$MFTF_APPLICATION_CONTEXT = + new MftfApplicationConfig($forceGenerate, $phase, $verboseEnabled, $debugEnabled); } } @@ -115,6 +130,17 @@ public function verboseEnabled() return $this->verboseEnabled ?? getenv('MFTF_DEBUG'); } + /** + * Returns a boolean indicating whether the user has indicated a debug run, which will lengthy validation + * with some extra error messaging to be run + * + * @return bool + */ + public function debugEnabled() + { + return $this->debugEnabled ?? getenv('MFTF_DEBUG'); + } + /** * Returns a string which indicates the phase of mftf execution. * diff --git a/src/Magento/FunctionalTestingFramework/Config/Reader/Filesystem.php b/src/Magento/FunctionalTestingFramework/Config/Reader/Filesystem.php index adcb330ba..9ed63798f 100644 --- a/src/Magento/FunctionalTestingFramework/Config/Reader/Filesystem.php +++ b/src/Magento/FunctionalTestingFramework/Config/Reader/Filesystem.php @@ -150,6 +150,13 @@ protected function readFiles($fileList) } else { $configMerger->merge($content); } + if ($this->validationState->isValidationRequired()) { + $errors = []; + if ($configMerger && !$configMerger->validate($this->schemaFile, $errors)) { + $message = $fileList->getFilename() . PHP_EOL . "Invalid Document \n"; + throw new \Exception($message . implode("\n", $errors)); + } + } } catch (\Magento\FunctionalTestingFramework\Config\Dom\ValidationException $e) { throw new \Exception("Invalid XML in file " . $fileList->getFilename() . ":\n" . $e->getMessage()); } diff --git a/src/Magento/FunctionalTestingFramework/Config/Reader/MftfFilesystem.php b/src/Magento/FunctionalTestingFramework/Config/Reader/MftfFilesystem.php index e39e39199..b65b7886c 100644 --- a/src/Magento/FunctionalTestingFramework/Config/Reader/MftfFilesystem.php +++ b/src/Magento/FunctionalTestingFramework/Config/Reader/MftfFilesystem.php @@ -6,6 +6,7 @@ namespace Magento\FunctionalTestingFramework\Config\Reader; +use Magento\FunctionalTestingFramework\Config\MftfApplicationConfig; use Magento\FunctionalTestingFramework\Exceptions\Collector\ExceptionCollector; use Magento\FunctionalTestingFramework\Util\Iterator\File; @@ -36,6 +37,14 @@ public function readFiles($fileList) } else { $configMerger->merge($content, $fileList->getFilename(), $exceptionCollector); } + if (MftfApplicationConfig::getConfig()->debugEnabled()) { + if ($this->validationState->isValidationRequired()) { + if ($configMerger && !$configMerger->validate($this->schemaFile, $errors)) { + $message = "Invalid Document: " . PHP_EOL . $fileList->getFilename() . PHP_EOL; + throw new \Exception($message . implode("\n", $errors)); + } + } + } } catch (\Magento\FunctionalTestingFramework\Config\Dom\ValidationException $e) { throw new \Exception("Invalid XML in file " . $key . ":\n" . $e->getMessage()); } @@ -43,7 +52,7 @@ public function readFiles($fileList) $exceptionCollector->throwException(); if ($this->validationState->isValidationRequired()) { if ($configMerger && !$configMerger->validate($this->schemaFile, $errors)) { - $message = "Invalid Document \n"; + $message = "Invalid Document: " . PHP_EOL; throw new \Exception($message . implode("\n", $errors)); } } diff --git a/src/Magento/FunctionalTestingFramework/Console/GenerateTestsCommand.php b/src/Magento/FunctionalTestingFramework/Console/GenerateTestsCommand.php index a8d34f1c9..912dca4a8 100644 --- a/src/Magento/FunctionalTestingFramework/Console/GenerateTestsCommand.php +++ b/src/Magento/FunctionalTestingFramework/Console/GenerateTestsCommand.php @@ -36,7 +36,8 @@ protected function configure() ->addOption("config", 'c', InputOption::VALUE_REQUIRED, 'default, singleRun, or parallel', 'default') ->addOption("force", 'f',InputOption::VALUE_NONE, 'force generation of tests regardless of Magento Instance Configuration') ->addOption('lines', 'l', InputOption::VALUE_REQUIRED, 'Used in combination with a parallel configuration, determines desired group size', 500) - ->addOption('tests', 't', InputOption::VALUE_REQUIRED, 'A parameter accepting a JSON string used to determine the test configuration'); + ->addOption('tests', 't', InputOption::VALUE_REQUIRED, 'A parameter accepting a JSON string used to determine the test configuration') + ->addOption('debug', 'd', InputOption::VALUE_NONE, 'run extra validation when generating tests'); } /** @@ -54,6 +55,7 @@ protected function execute(InputInterface $input, OutputInterface $output) $json = $input->getOption('tests'); $force = $input->getOption('force'); $lines = $input->getOption('lines'); + $debug = $input->getOption('debug'); $verbose = $output->isVerbose(); if ($json !== null && !json_decode($json)) { @@ -61,7 +63,7 @@ protected function execute(InputInterface $input, OutputInterface $output) throw new TestFrameworkException("JSON could not be parsed: " . json_last_error_msg()); } - $testConfiguration = $this->createTestConfiguration($json, $tests, $force, $verbose); + $testConfiguration = $this->createTestConfiguration($json, $tests, $force, $debug, $verbose); // create our manifest file here $testManifest = TestManifestFactory::makeManifest($config, $testConfiguration['suites']); @@ -84,16 +86,18 @@ protected function execute(InputInterface $input, OutputInterface $output) * @param string $json * @param array $tests * @param bool $force + * @param bool $debug * @param bool $verbose * @return array */ - private function createTestConfiguration($json, array $tests, bool $force, bool $verbose) + private function createTestConfiguration($json, array $tests, bool $force, bool $debug, bool $verbose) { // set our application configuration so we can references the user options in our framework MftfApplicationConfig::create( $force, MftfApplicationConfig::GENERATION_PHASE, - $verbose + $verbose, + $debug ); $testConfiguration = []; From c7200d3339720e72424b447273425cf8f2cab046 Mon Sep 17 00:00:00 2001 From: Alex Calandra <acalandra@magento.com> Date: Thu, 7 Jun 2018 09:40:03 -0500 Subject: [PATCH 051/111] MQE-1031: Empty files are flagged during generation or dealt with gracefully - Changed method name as requested --- .../FunctionalTestingFramework/Config/Reader/Filesystem.php | 4 ++-- .../Config/Reader/MftfFilesystem.php | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Magento/FunctionalTestingFramework/Config/Reader/Filesystem.php b/src/Magento/FunctionalTestingFramework/Config/Reader/Filesystem.php index 0b4f82546..e287a84d9 100644 --- a/src/Magento/FunctionalTestingFramework/Config/Reader/Filesystem.php +++ b/src/Magento/FunctionalTestingFramework/Config/Reader/Filesystem.php @@ -148,7 +148,7 @@ protected function readFiles($fileList) $configMerger = null; foreach ($fileList as $key => $content) { //check if file is empty and continue to next if it is - if (!$this->verifyFileStatus($content, $fileList->getFilename())) { + if (!$this->verifyFileEmpty($content, $fileList->getFilename())) { continue; } try { @@ -207,7 +207,7 @@ protected function createConfigMerger($mergerClass, $initialContents) * @param string $fileName * @return bool */ - protected function verifyFileStatus($content, $fileName) + protected function verifyFileEmpty($content, $fileName) { if (empty($content)) { if (MftfApplicationConfig::getConfig()->verboseEnabled()) { diff --git a/src/Magento/FunctionalTestingFramework/Config/Reader/MftfFilesystem.php b/src/Magento/FunctionalTestingFramework/Config/Reader/MftfFilesystem.php index 9223f3f3f..1106ec9b7 100644 --- a/src/Magento/FunctionalTestingFramework/Config/Reader/MftfFilesystem.php +++ b/src/Magento/FunctionalTestingFramework/Config/Reader/MftfFilesystem.php @@ -26,7 +26,7 @@ public function readFiles($fileList) $configMerger = null; foreach ($fileList as $key => $content) { //check if file is empty and continue to next if it is - if (!parent::verifyFileStatus($content, $fileList->getFilename())) { + if (!parent::verifyFileEmpty($content, $fileList->getFilename())) { continue; } try { From 5afa47ef4e7b9f9dadae9414552958d047976c32 Mon Sep 17 00:00:00 2001 From: Alex Calandra <acalandra@magento.com> Date: Thu, 7 Jun 2018 11:19:47 -0500 Subject: [PATCH 052/111] MQE-1017: Better Error Messaging When Non-Whitespace Characters Are Outside XML Elements - Abstracted validation code in Filesystem --- .../Config/Reader/Filesystem.php | 37 ++++++++++++------- .../Config/Reader/MftfFilesystem.php | 15 +------- 2 files changed, 26 insertions(+), 26 deletions(-) diff --git a/src/Magento/FunctionalTestingFramework/Config/Reader/Filesystem.php b/src/Magento/FunctionalTestingFramework/Config/Reader/Filesystem.php index 9ed63798f..224b3a2d5 100644 --- a/src/Magento/FunctionalTestingFramework/Config/Reader/Filesystem.php +++ b/src/Magento/FunctionalTestingFramework/Config/Reader/Filesystem.php @@ -5,6 +5,8 @@ */ namespace Magento\FunctionalTestingFramework\Config\Reader; +use Magento\FunctionalTestingFramework\Config\MftfApplicationConfig; + /** * Filesystem configuration loader. Loads configuration from XML files, split by scopes. */ @@ -150,24 +152,14 @@ protected function readFiles($fileList) } else { $configMerger->merge($content); } - if ($this->validationState->isValidationRequired()) { - $errors = []; - if ($configMerger && !$configMerger->validate($this->schemaFile, $errors)) { - $message = $fileList->getFilename() . PHP_EOL . "Invalid Document \n"; - throw new \Exception($message . implode("\n", $errors)); - } + if (MftfApplicationConfig::getConfig()->debugEnabled()) { + $this->validateSchema($configMerger, $fileList->getFilename()); } } catch (\Magento\FunctionalTestingFramework\Config\Dom\ValidationException $e) { throw new \Exception("Invalid XML in file " . $fileList->getFilename() . ":\n" . $e->getMessage()); } } - if ($this->validationState->isValidationRequired()) { - $errors = []; - if ($configMerger && !$configMerger->validate($this->schemaFile, $errors)) { - $message = "Invalid Document \n"; - throw new \Exception($message . implode("\n", $errors)); - } - } + $this->validateSchema($configMerger); $output = []; if ($configMerger) { @@ -199,4 +191,23 @@ protected function createConfigMerger($mergerClass, $initialContents) } return $result; } + + /** + * Validate read xml against expected schema + * + * @param string $configMerger + * @param string $filename + * @throws \Exception + * @return void + */ + protected function validateSchema($configMerger, $filename = null) + { + if ($this->validationState->isValidationRequired()) { + $errors = []; + if ($configMerger && !$configMerger->validate($this->schemaFile, $errors)) { + $message = $filename ? $filename . PHP_EOL . "Invalid Document \n" : PHP_EOL . "Invalid Document \n"; + throw new \Exception($message . implode("\n", $errors)); + } + } + } } diff --git a/src/Magento/FunctionalTestingFramework/Config/Reader/MftfFilesystem.php b/src/Magento/FunctionalTestingFramework/Config/Reader/MftfFilesystem.php index b65b7886c..8d0fcd373 100644 --- a/src/Magento/FunctionalTestingFramework/Config/Reader/MftfFilesystem.php +++ b/src/Magento/FunctionalTestingFramework/Config/Reader/MftfFilesystem.php @@ -22,7 +22,6 @@ class MftfFilesystem extends \Magento\FunctionalTestingFramework\Config\Reader\F public function readFiles($fileList) { $exceptionCollector = new ExceptionCollector(); - $errors = []; /** @var \Magento\FunctionalTestingFramework\Test\Config\Dom $configMerger */ $configMerger = null; foreach ($fileList as $key => $content) { @@ -38,24 +37,14 @@ public function readFiles($fileList) $configMerger->merge($content, $fileList->getFilename(), $exceptionCollector); } if (MftfApplicationConfig::getConfig()->debugEnabled()) { - if ($this->validationState->isValidationRequired()) { - if ($configMerger && !$configMerger->validate($this->schemaFile, $errors)) { - $message = "Invalid Document: " . PHP_EOL . $fileList->getFilename() . PHP_EOL; - throw new \Exception($message . implode("\n", $errors)); - } - } + $this->validateSchema($configMerger, $fileList->getFilename()); } } catch (\Magento\FunctionalTestingFramework\Config\Dom\ValidationException $e) { throw new \Exception("Invalid XML in file " . $key . ":\n" . $e->getMessage()); } } $exceptionCollector->throwException(); - if ($this->validationState->isValidationRequired()) { - if ($configMerger && !$configMerger->validate($this->schemaFile, $errors)) { - $message = "Invalid Document: " . PHP_EOL; - throw new \Exception($message . implode("\n", $errors)); - } - } + $this->validateSchema($configMerger, $fileList->getFilename()); $output = []; if ($configMerger) { From eac8f86f426ac39f7a9df3fb34e5f55d9711ee82 Mon Sep 17 00:00:00 2001 From: Alex Calandra <acalandra@magento.com> Date: Fri, 8 Jun 2018 13:56:33 -0500 Subject: [PATCH 053/111] MQE-1017: Better Error Messaging When Non-Whitespace Characters Are Outside XML Elements - Adding verification test for filesystem to check error --- dev/tests/util/MftfTestCase.php | 64 ++++++++++++++++++- .../TestModule/Test/PageReplacementTest.xml | 3 - .../Tests/SchemaValidationTest.php | 30 +++++++++ 3 files changed, 92 insertions(+), 5 deletions(-) create mode 100644 dev/tests/verification/Tests/SchemaValidationTest.php diff --git a/dev/tests/util/MftfTestCase.php b/dev/tests/util/MftfTestCase.php index e96c75fde..8bfa1009f 100644 --- a/dev/tests/util/MftfTestCase.php +++ b/dev/tests/util/MftfTestCase.php @@ -5,13 +5,20 @@ */ namespace tests\util; +use Magento\FunctionalTestingFramework\ObjectManager; use Magento\FunctionalTestingFramework\Test\Handlers\TestObjectHandler; use Magento\FunctionalTestingFramework\Util\TestGenerator; use PHPUnit\Framework\TestCase; abstract class MftfTestCase extends TestCase { - const RESOURCES_PATH = __DIR__ . '/../verification/Resources'; + const RESOURCES_PATH = __DIR__ . + DIRECTORY_SEPARATOR . + '..' . + DIRECTORY_SEPARATOR . + 'verification' . + DIRECTORY_SEPARATOR . + 'Resources'; /** * Private function which takes a test name, generates the test and compares with a correspondingly named txt file @@ -37,4 +44,57 @@ public function generateAndCompareTest($testName) $cestFile ); } -} \ No newline at end of file + + /** + * Private function which attempts to generate tests given an invalid shcema of a various type + * + * @param string[] $fileContents + * @param string $objectType + * @param string $expectedError + * @throws \Exception + */ + public function validateSchemaErrorWithTest($fileContents, $objectType ,$expectedError) + { + $this->clearHandler(); + $fullTestModulePath = TESTS_MODULE_PATH . + DIRECTORY_SEPARATOR . + 'TestModule' . + DIRECTORY_SEPARATOR . + $objectType . + DIRECTORY_SEPARATOR; + + foreach ($fileContents as $fileName => $fileContent) { + $tempFile = $fullTestModulePath . $fileName; + $handle = fopen($tempFile, 'w') or die('Cannot open file: ' . $tempFile); + fwrite($handle, $fileContent); + fclose($handle); + } + try { + $this->expectExceptionMessage($expectedError); + TestObjectHandler::getInstance()->getObject("someTest"); + } finally { + foreach (array_keys($fileContents) as $fileName) { + unlink($fullTestModulePath . $fileName); + } + $this->clearHandler(); + } + } + + /** + * Clears test handler and object manager to force recollection of test data + * + * @throws \Exception + */ + private function clearHandler() + { + // clear test object handler to force recollection of test data + $property = new \ReflectionProperty(TestObjectHandler::class, 'testObjectHandler'); + $property->setAccessible(true); + $property->setValue(null); + + // clear test object handler to force recollection of test data + $property = new \ReflectionProperty(ObjectManager::class, 'instance'); + $property->setAccessible(true); + $property->setValue(null); + } +} diff --git a/dev/tests/verification/TestModule/Test/PageReplacementTest.xml b/dev/tests/verification/TestModule/Test/PageReplacementTest.xml index acdbbb798..b6adee5d7 100644 --- a/dev/tests/verification/TestModule/Test/PageReplacementTest.xml +++ b/dev/tests/verification/TestModule/Test/PageReplacementTest.xml @@ -22,7 +22,4 @@ <amOnPage stepKey="oneParamAdminPageString" url="{{AdminOneParamPage.url('StringLiteral')}}"/> <amOnUrl stepKey="onExternalPage" url="{{ExternalPage.url}}"/> </test> - <test name="ExternalPageTestBadReference"> - <amOnPage stepKey="onExternalPage" url="{{ExternalPage.url}}"/> - </test> </tests> \ No newline at end of file diff --git a/dev/tests/verification/Tests/SchemaValidationTest.php b/dev/tests/verification/Tests/SchemaValidationTest.php new file mode 100644 index 000000000..7d247693e --- /dev/null +++ b/dev/tests/verification/Tests/SchemaValidationTest.php @@ -0,0 +1,30 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +namespace tests\verification\Tests; + +use tests\util\MftfTestCase; + +class SchemaValidationTest extends MftfTestCase +{ + /** + * Test generation of a test referencing an action group with no arguments + * + * @throws \Exception + * @throws \Magento\FunctionalTestingFramework\Exceptions\TestReferenceException + */ + public function testInvalidTestSchema() + { + $testFile = ['testFile.xml' => "<tests><test name='testName'><annotations>a</annotations></test></tests>"]; + $expectedError = TESTS_MODULE_PATH . + DIRECTORY_SEPARATOR . + "TestModule" . + DIRECTORY_SEPARATOR . + "Test" . + DIRECTORY_SEPARATOR . + "testFile.xml"; + $this->validateSchemaErrorWithTest($testFile, 'Test', $expectedError); + } +} From 5dc4c0dfbb0e6bbc1333222e9dd75bc79fb2d905 Mon Sep 17 00:00:00 2001 From: Kevin Kozan <kkozan@magento.com> Date: Mon, 11 Jun 2018 16:38:08 +0300 Subject: [PATCH 054/111] MQE-1027: Allure report requires a unique story/title for proper reporting - Added validation of Story/Title combinations for uniqueness --- .gitignore | 1 + .../ActionGroupWithDataOverrideTest.txt | 1 - .../Resources/ActionGroupWithDataTest.txt | 1 - .../ActionGroupWithNoDefaultTest.txt | 1 - .../ActionGroupWithPersistedData.txt | 1 - .../ActionGroupWithTopLevelPersistedData.txt | 1 - .../ArgumentWithSameNameAsElement.txt | 1 - .../Resources/BasicActionGroupTest.txt | 1 - .../verification/Resources/BasicMergeTest.txt | 2 +- .../Resources/ChildExtendedTestAddHooks.txt | 2 +- .../Resources/ChildExtendedTestMerging.txt | 2 +- .../Resources/ChildExtendedTestNoParent.txt | 2 +- .../ChildExtendedTestRemoveAction.txt | 2 +- .../ChildExtendedTestRemoveHookAction.txt | 2 +- .../Resources/ChildExtendedTestReplace.txt | 2 +- .../ChildExtendedTestReplaceHook.txt | 2 +- .../Resources/MergedActionGroupTest.txt | 1 - .../Resources/MergedReferencesTest.txt | 2 +- .../Resources/MultipleActionGroupsTest.txt | 1 - .../Resources/ParentExtendedTest.txt | 2 +- .../Test/ActionGroupFunctionalTest.xml | 9 --- .../Test/ExtendedFunctionalTest.xml | 18 +++--- .../TestModule/Test/MergeFunctionalTest.xml | 4 +- .../Test/Handlers/TestObjectHandler.php | 2 + .../Test/Util/AnnotationExtractor.php | 57 ++++++++++++++++++- .../Test/Util/TestObjectExtractor.php | 14 ++++- 26 files changed, 91 insertions(+), 43 deletions(-) diff --git a/.gitignore b/.gitignore index c1adae14d..d39929c6f 100755 --- a/.gitignore +++ b/.gitignore @@ -14,4 +14,5 @@ coverage/ codeception.yml dev/tests/functional/MFTF.suite.yml dev/tests/functional/_output +dev/mftf.log dev/tests/mftf.log \ No newline at end of file diff --git a/dev/tests/verification/Resources/ActionGroupWithDataOverrideTest.txt b/dev/tests/verification/Resources/ActionGroupWithDataOverrideTest.txt index 0c4ff37af..c939f421e 100644 --- a/dev/tests/verification/Resources/ActionGroupWithDataOverrideTest.txt +++ b/dev/tests/verification/Resources/ActionGroupWithDataOverrideTest.txt @@ -16,7 +16,6 @@ use Yandex\Allure\Adapter\Model\SeverityLevel; use Yandex\Allure\Adapter\Annotation\TestCaseId; /** - * @Title("A Functional Cest") * @group functional */ class ActionGroupWithDataOverrideTestCest diff --git a/dev/tests/verification/Resources/ActionGroupWithDataTest.txt b/dev/tests/verification/Resources/ActionGroupWithDataTest.txt index 5f7d75899..d11eff9a8 100644 --- a/dev/tests/verification/Resources/ActionGroupWithDataTest.txt +++ b/dev/tests/verification/Resources/ActionGroupWithDataTest.txt @@ -16,7 +16,6 @@ use Yandex\Allure\Adapter\Model\SeverityLevel; use Yandex\Allure\Adapter\Annotation\TestCaseId; /** - * @Title("A Functional Cest") * @group functional */ class ActionGroupWithDataTestCest diff --git a/dev/tests/verification/Resources/ActionGroupWithNoDefaultTest.txt b/dev/tests/verification/Resources/ActionGroupWithNoDefaultTest.txt index 82fde4afd..8739d8fa0 100644 --- a/dev/tests/verification/Resources/ActionGroupWithNoDefaultTest.txt +++ b/dev/tests/verification/Resources/ActionGroupWithNoDefaultTest.txt @@ -16,7 +16,6 @@ use Yandex\Allure\Adapter\Model\SeverityLevel; use Yandex\Allure\Adapter\Annotation\TestCaseId; /** - * @Title("A Functional Cest") * @group functional */ class ActionGroupWithNoDefaultTestCest diff --git a/dev/tests/verification/Resources/ActionGroupWithPersistedData.txt b/dev/tests/verification/Resources/ActionGroupWithPersistedData.txt index fcd4f74f9..63fd6af52 100644 --- a/dev/tests/verification/Resources/ActionGroupWithPersistedData.txt +++ b/dev/tests/verification/Resources/ActionGroupWithPersistedData.txt @@ -16,7 +16,6 @@ use Yandex\Allure\Adapter\Model\SeverityLevel; use Yandex\Allure\Adapter\Annotation\TestCaseId; /** - * @Title("A Functional Cest") * @group functional */ class ActionGroupWithPersistedDataCest diff --git a/dev/tests/verification/Resources/ActionGroupWithTopLevelPersistedData.txt b/dev/tests/verification/Resources/ActionGroupWithTopLevelPersistedData.txt index 6588d490b..1881a392a 100644 --- a/dev/tests/verification/Resources/ActionGroupWithTopLevelPersistedData.txt +++ b/dev/tests/verification/Resources/ActionGroupWithTopLevelPersistedData.txt @@ -16,7 +16,6 @@ use Yandex\Allure\Adapter\Model\SeverityLevel; use Yandex\Allure\Adapter\Annotation\TestCaseId; /** - * @Title("A Functional Cest") * @group functional */ class ActionGroupWithTopLevelPersistedDataCest diff --git a/dev/tests/verification/Resources/ArgumentWithSameNameAsElement.txt b/dev/tests/verification/Resources/ArgumentWithSameNameAsElement.txt index bc2890967..90b195688 100644 --- a/dev/tests/verification/Resources/ArgumentWithSameNameAsElement.txt +++ b/dev/tests/verification/Resources/ArgumentWithSameNameAsElement.txt @@ -16,7 +16,6 @@ use Yandex\Allure\Adapter\Model\SeverityLevel; use Yandex\Allure\Adapter\Annotation\TestCaseId; /** - * @Title("A Functional Cest") * @group functional */ class ArgumentWithSameNameAsElementCest diff --git a/dev/tests/verification/Resources/BasicActionGroupTest.txt b/dev/tests/verification/Resources/BasicActionGroupTest.txt index 45ccc72a6..259016681 100644 --- a/dev/tests/verification/Resources/BasicActionGroupTest.txt +++ b/dev/tests/verification/Resources/BasicActionGroupTest.txt @@ -16,7 +16,6 @@ use Yandex\Allure\Adapter\Model\SeverityLevel; use Yandex\Allure\Adapter\Annotation\TestCaseId; /** - * @Title("A Functional Cest") * @group functional */ class BasicActionGroupTestCest diff --git a/dev/tests/verification/Resources/BasicMergeTest.txt b/dev/tests/verification/Resources/BasicMergeTest.txt index ae3a69096..ad5a0c189 100644 --- a/dev/tests/verification/Resources/BasicMergeTest.txt +++ b/dev/tests/verification/Resources/BasicMergeTest.txt @@ -16,7 +16,7 @@ use Yandex\Allure\Adapter\Model\SeverityLevel; use Yandex\Allure\Adapter\Annotation\TestCaseId; /** - * @Title("A Functional Cest") + * @Title("BasicMergeTest") * @group functional * @group mergeTest */ diff --git a/dev/tests/verification/Resources/ChildExtendedTestAddHooks.txt b/dev/tests/verification/Resources/ChildExtendedTestAddHooks.txt index 3b7015529..bf3d3a8c2 100644 --- a/dev/tests/verification/Resources/ChildExtendedTestAddHooks.txt +++ b/dev/tests/verification/Resources/ChildExtendedTestAddHooks.txt @@ -16,7 +16,7 @@ use Yandex\Allure\Adapter\Model\SeverityLevel; use Yandex\Allure\Adapter\Annotation\TestCaseId; /** - * @Title("Parent") + * @Title("ChildExtendedTestAddHooks") * @group Parent */ class ChildExtendedTestAddHooksCest diff --git a/dev/tests/verification/Resources/ChildExtendedTestMerging.txt b/dev/tests/verification/Resources/ChildExtendedTestMerging.txt index 90943edfc..1efe59cb5 100644 --- a/dev/tests/verification/Resources/ChildExtendedTestMerging.txt +++ b/dev/tests/verification/Resources/ChildExtendedTestMerging.txt @@ -16,7 +16,7 @@ use Yandex\Allure\Adapter\Model\SeverityLevel; use Yandex\Allure\Adapter\Annotation\TestCaseId; /** - * @Title("Child") + * @Title("ChildExtendedTestMerging") * @group Child */ class ChildExtendedTestMergingCest diff --git a/dev/tests/verification/Resources/ChildExtendedTestNoParent.txt b/dev/tests/verification/Resources/ChildExtendedTestNoParent.txt index 007db6bf5..3bc7b8a0a 100644 --- a/dev/tests/verification/Resources/ChildExtendedTestNoParent.txt +++ b/dev/tests/verification/Resources/ChildExtendedTestNoParent.txt @@ -16,7 +16,7 @@ use Yandex\Allure\Adapter\Model\SeverityLevel; use Yandex\Allure\Adapter\Annotation\TestCaseId; /** - * @Title("Child") + * @Title("ChildExtendedTestNoParent") * @group Child * @group skip */ diff --git a/dev/tests/verification/Resources/ChildExtendedTestRemoveAction.txt b/dev/tests/verification/Resources/ChildExtendedTestRemoveAction.txt index a9ef163b4..f24c3e6d1 100644 --- a/dev/tests/verification/Resources/ChildExtendedTestRemoveAction.txt +++ b/dev/tests/verification/Resources/ChildExtendedTestRemoveAction.txt @@ -16,7 +16,7 @@ use Yandex\Allure\Adapter\Model\SeverityLevel; use Yandex\Allure\Adapter\Annotation\TestCaseId; /** - * @Title("Child") + * @Title("ChildExtendedTestRemoveAction") * @group Child */ class ChildExtendedTestRemoveActionCest diff --git a/dev/tests/verification/Resources/ChildExtendedTestRemoveHookAction.txt b/dev/tests/verification/Resources/ChildExtendedTestRemoveHookAction.txt index 4a778a9fc..9d207e13f 100644 --- a/dev/tests/verification/Resources/ChildExtendedTestRemoveHookAction.txt +++ b/dev/tests/verification/Resources/ChildExtendedTestRemoveHookAction.txt @@ -16,7 +16,7 @@ use Yandex\Allure\Adapter\Model\SeverityLevel; use Yandex\Allure\Adapter\Annotation\TestCaseId; /** - * @Title("Child") + * @Title("ChildExtendedTestRemoveHookAction") * @group Child */ class ChildExtendedTestRemoveHookActionCest diff --git a/dev/tests/verification/Resources/ChildExtendedTestReplace.txt b/dev/tests/verification/Resources/ChildExtendedTestReplace.txt index b41a22323..6f3c3c9b3 100644 --- a/dev/tests/verification/Resources/ChildExtendedTestReplace.txt +++ b/dev/tests/verification/Resources/ChildExtendedTestReplace.txt @@ -16,7 +16,7 @@ use Yandex\Allure\Adapter\Model\SeverityLevel; use Yandex\Allure\Adapter\Annotation\TestCaseId; /** - * @Title("Child") + * @Title("ChildExtendedTestReplace") * @group Child */ class ChildExtendedTestReplaceCest diff --git a/dev/tests/verification/Resources/ChildExtendedTestReplaceHook.txt b/dev/tests/verification/Resources/ChildExtendedTestReplaceHook.txt index 384aa8d5f..2a5937381 100644 --- a/dev/tests/verification/Resources/ChildExtendedTestReplaceHook.txt +++ b/dev/tests/verification/Resources/ChildExtendedTestReplaceHook.txt @@ -16,7 +16,7 @@ use Yandex\Allure\Adapter\Model\SeverityLevel; use Yandex\Allure\Adapter\Annotation\TestCaseId; /** - * @Title("Child") + * @Title("ChildExtendedTestReplaceHook") * @group Child */ class ChildExtendedTestReplaceHookCest diff --git a/dev/tests/verification/Resources/MergedActionGroupTest.txt b/dev/tests/verification/Resources/MergedActionGroupTest.txt index f6c8f2351..f1d73dba5 100644 --- a/dev/tests/verification/Resources/MergedActionGroupTest.txt +++ b/dev/tests/verification/Resources/MergedActionGroupTest.txt @@ -16,7 +16,6 @@ use Yandex\Allure\Adapter\Model\SeverityLevel; use Yandex\Allure\Adapter\Annotation\TestCaseId; /** - * @Title("A Functional Cest") * @group functional */ class MergedActionGroupTestCest diff --git a/dev/tests/verification/Resources/MergedReferencesTest.txt b/dev/tests/verification/Resources/MergedReferencesTest.txt index 530a23b2e..fe0c9498f 100644 --- a/dev/tests/verification/Resources/MergedReferencesTest.txt +++ b/dev/tests/verification/Resources/MergedReferencesTest.txt @@ -16,7 +16,7 @@ use Yandex\Allure\Adapter\Model\SeverityLevel; use Yandex\Allure\Adapter\Annotation\TestCaseId; /** - * @Title("A Functional Cest") + * @Title("MergedReferencesTest") * @group functional */ class MergedReferencesTestCest diff --git a/dev/tests/verification/Resources/MultipleActionGroupsTest.txt b/dev/tests/verification/Resources/MultipleActionGroupsTest.txt index 8bb180f5d..18f6df3a9 100644 --- a/dev/tests/verification/Resources/MultipleActionGroupsTest.txt +++ b/dev/tests/verification/Resources/MultipleActionGroupsTest.txt @@ -16,7 +16,6 @@ use Yandex\Allure\Adapter\Model\SeverityLevel; use Yandex\Allure\Adapter\Annotation\TestCaseId; /** - * @Title("A Functional Cest") * @group functional */ class MultipleActionGroupsTestCest diff --git a/dev/tests/verification/Resources/ParentExtendedTest.txt b/dev/tests/verification/Resources/ParentExtendedTest.txt index 70ef6ada8..a79783217 100644 --- a/dev/tests/verification/Resources/ParentExtendedTest.txt +++ b/dev/tests/verification/Resources/ParentExtendedTest.txt @@ -16,7 +16,7 @@ use Yandex\Allure\Adapter\Model\SeverityLevel; use Yandex\Allure\Adapter\Annotation\TestCaseId; /** - * @Title("Parent") + * @Title("ParentExtendedTest") * @group Parent */ class ParentExtendedTestCest diff --git a/dev/tests/verification/TestModule/Test/ActionGroupFunctionalTest.xml b/dev/tests/verification/TestModule/Test/ActionGroupFunctionalTest.xml index 63f800983..8c24794e1 100644 --- a/dev/tests/verification/TestModule/Test/ActionGroupFunctionalTest.xml +++ b/dev/tests/verification/TestModule/Test/ActionGroupFunctionalTest.xml @@ -11,7 +11,6 @@ <test name="BasicActionGroupTest"> <annotations> <severity value="CRITICAL"/> - <title value="A Functional Cest"/> <group value="functional"/> <features value="Action Group Functional Cest"/> <stories value="MQE-433"/> @@ -27,7 +26,6 @@ <test name="ActionGroupWithDataTest"> <annotations> <severity value="CRITICAL"/> - <title value="A Functional Cest"/> <group value="functional"/> <features value="Action Group Functional Cest"/> <stories value="MQE-433"/> @@ -46,7 +44,6 @@ <test name="ActionGroupWithDataOverrideTest"> <annotations> <severity value="CRITICAL"/> - <title value="A Functional Cest"/> <group value="functional"/> <features value="Action Group Functional Cest"/> <stories value="MQE-433"/> @@ -67,7 +64,6 @@ <test name="ActionGroupWithNoDefaultTest"> <annotations> <severity value="CRITICAL"/> - <title value="A Functional Cest"/> <group value="functional"/> <features value="Action Group Functional Cest"/> <stories value="MQE-433"/> @@ -88,7 +84,6 @@ <test name="ActionGroupWithPersistedData"> <annotations> <severity value="CRITICAL"/> - <title value="A Functional Cest"/> <group value="functional"/> <features value="Action Group Functional Cest"/> <stories value="MQE-433"/> @@ -108,7 +103,6 @@ <test name="ActionGroupWithTopLevelPersistedData"> <annotations> <severity value="CRITICAL"/> - <title value="A Functional Cest"/> <group value="functional"/> <features value="Action Group Functional Cest"/> <stories value="MQE-433"/> @@ -127,7 +121,6 @@ <test name="MultipleActionGroupsTest"> <annotations> <severity value="CRITICAL"/> - <title value="A Functional Cest"/> <group value="functional"/> <features value="Action Group Functional Cest"/> <stories value="MQE-433"/> @@ -149,7 +142,6 @@ <test name="MergedActionGroupTest"> <annotations> <severity value="CRITICAL"/> - <title value="A Functional Cest"/> <group value="functional"/> <features value="Action Group Functional Cest"/> <stories value="MQE-433"/> @@ -168,7 +160,6 @@ <test name="ArgumentWithSameNameAsElement"> <annotations> <severity value="CRITICAL"/> - <title value="A Functional Cest"/> <group value="functional"/> <features value="Action Group Functional Cest"/> <stories value="MQE-433"/> diff --git a/dev/tests/verification/TestModule/Test/ExtendedFunctionalTest.xml b/dev/tests/verification/TestModule/Test/ExtendedFunctionalTest.xml index dcf929188..602b9a02c 100644 --- a/dev/tests/verification/TestModule/Test/ExtendedFunctionalTest.xml +++ b/dev/tests/verification/TestModule/Test/ExtendedFunctionalTest.xml @@ -11,7 +11,7 @@ <test name="ParentExtendedTest"> <annotations> <severity value="AVERAGE"/> - <title value="Parent"/> + <title value="ParentExtendedTest"/> <group value="Parent"/> <features value="Parent"/> <stories value="Parent"/> @@ -28,7 +28,7 @@ <test name="ChildExtendedTestReplace" extends="ParentExtendedTest"> <annotations> <severity value="MINOR"/> - <title value="Child"/> + <title value="ChildExtendedTestReplace"/> <group value="Child"/> <features value="Child"/> <stories value="Child"/> @@ -39,7 +39,7 @@ <test name="ChildExtendedTestReplaceHook" extends="ParentExtendedTest"> <annotations> <severity value="MINOR"/> - <title value="Child"/> + <title value="ChildExtendedTestReplaceHook"/> <group value="Child"/> <features value="Child"/> <stories value="Child"/> @@ -52,7 +52,7 @@ <test name="ChildExtendedTestMerging" extends="ParentExtendedTest"> <annotations> <severity value="MINOR"/> - <title value="Child"/> + <title value="ChildExtendedTestMerging"/> <group value="Child"/> <features value="Child"/> <stories value="Child"/> @@ -69,7 +69,7 @@ <test name="ChildExtendedTestRemoveAction" extends="ParentExtendedTest"> <annotations> <severity value="CRITICAL"/> - <title value="Child"/> + <title value="ChildExtendedTestRemoveAction"/> <group value="Child"/> <features value="Child"/> <stories value="Child"/> @@ -80,7 +80,7 @@ <test name="ParentExtendedTestNoHooks"> <annotations> <severity value="AVERAGE"/> - <title value="Parent"/> + <title value="ParentExtendedTestNoHooks"/> <group value="Parent"/> <features value="Parent"/> <stories value="Parent"/> @@ -91,7 +91,7 @@ <test name="ChildExtendedTestAddHooks"> <annotations> <severity value="AVERAGE"/> - <title value="Parent"/> + <title value="ChildExtendedTestAddHooks"/> <group value="Parent"/> <features value="Parent"/> <stories value="Parent"/> @@ -107,7 +107,7 @@ <test name="ChildExtendedTestRemoveHookAction" extends="ParentExtendedTest"> <annotations> <severity value="CRITICAL"/> - <title value="Child"/> + <title value="ChildExtendedTestRemoveHookAction"/> <group value="Child"/> <features value="Child"/> <stories value="Child"/> @@ -120,7 +120,7 @@ <test name="ChildExtendedTestNoParent" extends="ThisTestDoesNotExist"> <annotations> <severity value="CRITICAL"/> - <title value="Child"/> + <title value="ChildExtendedTestNoParent"/> <group value="Child"/> <features value="Child"/> <stories value="Child"/> diff --git a/dev/tests/verification/TestModule/Test/MergeFunctionalTest.xml b/dev/tests/verification/TestModule/Test/MergeFunctionalTest.xml index 553f3d251..f7997946c 100644 --- a/dev/tests/verification/TestModule/Test/MergeFunctionalTest.xml +++ b/dev/tests/verification/TestModule/Test/MergeFunctionalTest.xml @@ -11,7 +11,7 @@ <test name="BasicMergeTest"> <annotations> <severity value="CRITICAL"/> - <title value="A Functional Cest"/> + <title value="BasicMergeTest"/> <group value="functional"/> <features value="Merge Functional Cest"/> <stories value="MQE-433"/> @@ -31,7 +31,7 @@ <test name="MergedReferencesTest"> <annotations> <severity value="CRITICAL"/> - <title value="A Functional Cest"/> + <title value="MergedReferencesTest"/> <group value="functional"/> <features value="Merge Functional Cest"/> <stories value="MQE-433"/> diff --git a/src/Magento/FunctionalTestingFramework/Test/Handlers/TestObjectHandler.php b/src/Magento/FunctionalTestingFramework/Test/Handlers/TestObjectHandler.php index 025ebb079..e7ccc90ef 100644 --- a/src/Magento/FunctionalTestingFramework/Test/Handlers/TestObjectHandler.php +++ b/src/Magento/FunctionalTestingFramework/Test/Handlers/TestObjectHandler.php @@ -13,6 +13,7 @@ use Magento\FunctionalTestingFramework\Test\Parsers\TestDataParser; use Magento\FunctionalTestingFramework\Test\Util\ObjectExtensionUtil; use Magento\FunctionalTestingFramework\Test\Util\TestObjectExtractor; +use Magento\FunctionalTestingFramework\Test\Util\AnnotationExtractor; /** * Class TestObjectHandler @@ -142,6 +143,7 @@ private function initTestData() $this->tests[$testName] = $testObjectExtractor->extractTestData($testData); } + $testObjectExtractor->getAnnotationExtractor()->validateStoryTitleUniqueness(); } /** diff --git a/src/Magento/FunctionalTestingFramework/Test/Util/AnnotationExtractor.php b/src/Magento/FunctionalTestingFramework/Test/Util/AnnotationExtractor.php index 6e98c78f3..1718bf059 100644 --- a/src/Magento/FunctionalTestingFramework/Test/Util/AnnotationExtractor.php +++ b/src/Magento/FunctionalTestingFramework/Test/Util/AnnotationExtractor.php @@ -6,11 +6,20 @@ namespace Magento\FunctionalTestingFramework\Test\Util; +use Magento\FunctionalTestingFramework\Exceptions\XmlException; + /** * Class AnnotationExtractor */ class AnnotationExtractor extends BaseObjectExtractor { + /** + * Mappings of all Test => title mappings, indexed by Story + * e.g. $storyToTitleMappings['storyAnnotation'] = ['testName' => 'titleAnnotation'] + * @var array + */ + private $storyToTitleMappings = []; + const ANNOTATION_VALUE = 'value'; const MAGENTO_TO_ALLURE_SEVERITY_MAP = [ "BLOCKER" => "BLOCKER", @@ -33,11 +42,11 @@ public function __construct() * can be found in both Tests and their child element tests. * * @param array $testAnnotations + * @param string $filename * @return array */ - public function extractAnnotations($testAnnotations) + public function extractAnnotations($testAnnotations, $filename) { - $annotationObjects = []; $annotations = $this->stripDescriptorTags($testAnnotations, self::NODE_NAME); @@ -58,10 +67,52 @@ public function extractAnnotations($testAnnotations) } $annotationObjects[$annotationKey] = $annotationValues; } - + $this->addStoryTitleToMap($annotationObjects, $filename); return $annotationObjects; } + /** + * Adds story/title/filename combination to static map + * @param array $annotations + * @param string $filename + * @return void + */ + public function addStoryTitleToMap($annotations, $filename) + { + if (isset($annotations['stories']) && isset($annotations['title'])) { + $story = $annotations['stories'][0]; + $title = $annotations['title'][0]; + $this->storyToTitleMappings[$story . "/" . $title][] = $filename; + } + } + + /** + * Validates that all Story/Title combinations are unique, builds list of violators if found. + * @throws XmlException + * @return void + */ + public function validateStoryTitleUniqueness() + { + $dupes = []; + + foreach ($this->storyToTitleMappings as $storyTitle => $files) { + if (count($files) > 1) { + $dupes[$storyTitle] = "'" . implode("', '", $files) . "'"; + } + } + if (!empty($dupes)) { + $message = "Story and Title annotation pairs must be unique:\n\n"; + foreach ($dupes as $storyTitle => $tests) { + $storyTitleArray = explode("/", $storyTitle); + $story = $storyTitleArray[0]; + $title = $storyTitleArray[1]; + $message .= "Story: '{$story}' Title: '{$title}' in Tests {$tests}\n\n"; + + } + throw new XmlException($message); + } + } + /** * This method transforms Magento severity values from Severity annotation * Returns Allure annotation value diff --git a/src/Magento/FunctionalTestingFramework/Test/Util/TestObjectExtractor.php b/src/Magento/FunctionalTestingFramework/Test/Util/TestObjectExtractor.php index 5e58c4d53..0dcdedd7d 100644 --- a/src/Magento/FunctionalTestingFramework/Test/Util/TestObjectExtractor.php +++ b/src/Magento/FunctionalTestingFramework/Test/Util/TestObjectExtractor.php @@ -67,6 +67,15 @@ public function __construct() $this->modulePathExtractor = new ModulePathExtractor(); } + /** + * Getter for AnnotationExtractor + * @return AnnotationExtractor + */ + public function getAnnotationExtractor() + { + return $this->annotationExtractor; + } + /** * This method takes and array of test data and strips away irrelevant tags. The data is converted into an array of * TestObjects. @@ -102,7 +111,10 @@ public function extractTestData($testData) ); if (array_key_exists(self::TEST_ANNOTATIONS, $testData)) { - $testAnnotations = $this->annotationExtractor->extractAnnotations($testData[self::TEST_ANNOTATIONS]); + $testAnnotations = $this->annotationExtractor->extractAnnotations( + $testData[self::TEST_ANNOTATIONS], + $testData[self::NAME] + ); } //Override features with module name if present, populates it otherwise From ca4f32904fa734e4f748dd9c43f92b0c9d8d8b53 Mon Sep 17 00:00:00 2001 From: Alex Calandra <acalandra@magento.com> Date: Tue, 12 Jun 2018 12:59:11 -0500 Subject: [PATCH 055/111] MQE-1021: Empty Action StepKey Attribute Issues - Added check for empty stepkey and unit test --- .../Test/Util/ActionObjectExtractorTest.php | 9 +++++++++ .../Test/Util/ActionObjectExtractor.php | 5 +++++ 2 files changed, 14 insertions(+) diff --git a/dev/tests/unit/Magento/FunctionalTestFramework/Test/Util/ActionObjectExtractorTest.php b/dev/tests/unit/Magento/FunctionalTestFramework/Test/Util/ActionObjectExtractorTest.php index 7872b3a02..e78038568 100644 --- a/dev/tests/unit/Magento/FunctionalTestFramework/Test/Util/ActionObjectExtractorTest.php +++ b/dev/tests/unit/Magento/FunctionalTestFramework/Test/Util/ActionObjectExtractorTest.php @@ -89,6 +89,15 @@ public function testAmbiguousMergeOrderReference() ); } + /** + * Tests basic action object extraction with an empty stepKey + */ + public function testEmptyStepKey() + { + $this->expectExceptionMessage("StepKeys cannot be empty. Action='sampleAction'"); + $this->testActionObjectExtractor->extractActions($this->createBasicActionObjectArray("")); + } + /** * Utility function to return mock parser output for testing extraction into ActionObjects. * diff --git a/src/Magento/FunctionalTestingFramework/Test/Util/ActionObjectExtractor.php b/src/Magento/FunctionalTestingFramework/Test/Util/ActionObjectExtractor.php index 4ffd70ecf..d157ca5d6 100644 --- a/src/Magento/FunctionalTestingFramework/Test/Util/ActionObjectExtractor.php +++ b/src/Magento/FunctionalTestingFramework/Test/Util/ActionObjectExtractor.php @@ -27,6 +27,7 @@ class ActionObjectExtractor extends BaseObjectExtractor const ACTION_GROUP_ARG_VALUE = 'value'; const BEFORE_AFTER_ERROR_MSG = "Merge Error - Steps cannot have both before and after attributes.\tStepKey='%s'"; const STEP_KEY_BLACKLIST_ERROR_MSG = "StepKeys cannot contain non alphanumeric characters.\tStepKey='%s'"; + const STEP_KEY_EMPTY_ERROR_MSG = "StepKeys cannot be empty.\tAction='%s'"; const DATA_PERSISTENCE_CUSTOM_FIELD = 'field'; const DATA_PERSISTENCE_CUSTOM_FIELD_KEY = 'key'; const ACTION_OBJECT_PERSISTENCE_FIELDS = 'customFields'; @@ -59,6 +60,10 @@ public function extractActions($testActions, $testName = null) foreach ($testActions as $actionName => $actionData) { $stepKey = $actionData[self::TEST_STEP_MERGE_KEY]; + if (empty($stepKey)) { + throw new XmlException(sprintf(self::STEP_KEY_EMPTY_ERROR_MSG, $actionData['nodeName'])); + } + if (preg_match('/[^a-zA-Z0-9_]/', $stepKey)) { throw new XmlException(sprintf(self::STEP_KEY_BLACKLIST_ERROR_MSG, $actionName)); } From 1099c00724717e11ac0093d32815db9a73a913e8 Mon Sep 17 00:00:00 2001 From: Alex Calandra <acalandra@magento.com> Date: Tue, 12 Jun 2018 13:20:10 -0500 Subject: [PATCH 056/111] MQE-1021: Empty Action StepKey Attribute Issues - Fixes other unit test --- .../Test/Util/ActionObjectExtractorTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dev/tests/unit/Magento/FunctionalTestFramework/Test/Util/ActionObjectExtractorTest.php b/dev/tests/unit/Magento/FunctionalTestFramework/Test/Util/ActionObjectExtractorTest.php index e78038568..1d645ab62 100644 --- a/dev/tests/unit/Magento/FunctionalTestFramework/Test/Util/ActionObjectExtractorTest.php +++ b/dev/tests/unit/Magento/FunctionalTestFramework/Test/Util/ActionObjectExtractorTest.php @@ -50,7 +50,7 @@ public function testInvalidMergeOrderReference() } catch (\Exception $e) { TestLoggingUtil::getInstance()->validateMockLogStatement( 'error', - 'Line 103: Invalid ordering configuration in test', + 'Line 108: Invalid ordering configuration in test', [ 'test' => 'TestWithSelfReferencingStepKey', 'stepKey' => ['invalidTestAction1'] From 37de3a17802029a01565c2d21c9123805a0a570e Mon Sep 17 00:00:00 2001 From: Kevin Kozan <kkozan@magento.com> Date: Tue, 12 Jun 2018 21:26:09 +0300 Subject: [PATCH 057/111] MQE-1015: Test Generation Error If Suite Has Action Group With 2+ Arguments - fixed di.xml problem - added to existing suite generation tests --- .../verification/Resources/functionalSuiteHooks.txt | 2 ++ .../TestModule/ActionGroup/BasicActionGroup.xml | 9 +++++++++ dev/tests/verification/_suite/functionalSuiteHooks.xml | 8 ++++++++ etc/di.xml | 2 ++ 4 files changed, 21 insertions(+) diff --git a/dev/tests/verification/Resources/functionalSuiteHooks.txt b/dev/tests/verification/Resources/functionalSuiteHooks.txt index 594cbc383..8ec37451c 100644 --- a/dev/tests/verification/Resources/functionalSuiteHooks.txt +++ b/dev/tests/verification/Resources/functionalSuiteHooks.txt @@ -58,6 +58,7 @@ class functionalSuiteHooks extends \Codeception\GroupObject $createThis = DataObjectHandler::getInstance()->getObject("createThis"); $this->create = new DataPersistenceHandler($createThis, [], $createFields); $this->create->createEntity(); + $webDriver->see("John", msq("uniqueData") . "John"); // reset configuration and close session $this->getModule('\Magento\FunctionalTestingFramework\Module\MagentoWebDriver')->_resetConfig(); @@ -95,6 +96,7 @@ class functionalSuiteHooks extends \Codeception\GroupObject $webDriver->_initializeSession(); $webDriver->amOnPage("some.url"); $webDriver->deleteEntityByUrl("deleteThis"); + $webDriver->see("John", msq("uniqueData") . "John"); // reset configuration and close session $this->getModule('\Magento\FunctionalTestingFramework\Module\MagentoWebDriver')->_resetConfig(); diff --git a/dev/tests/verification/TestModule/ActionGroup/BasicActionGroup.xml b/dev/tests/verification/TestModule/ActionGroup/BasicActionGroup.xml index 30c0c03c7..5b5ddf775 100644 --- a/dev/tests/verification/TestModule/ActionGroup/BasicActionGroup.xml +++ b/dev/tests/verification/TestModule/ActionGroup/BasicActionGroup.xml @@ -19,6 +19,15 @@ <see selector="{{SampleSection.oneParamElement('test1')}}" userInput="{{someArgument.firstname}}" stepKey="seeFirstName" /> </actionGroup> + <actionGroup name="actionGroupWithTwoArguments"> + <arguments> + <argument name="somePerson"/> + <argument name="anotherPerson"/> + </arguments> + + <see selector="{{anotherPerson.firstname}}" userInput="{{somePerson.firstname}}" stepKey="seeFirstName" /> + </actionGroup> + <actionGroup name="actionGroupWithSingleParameterSelectorFromArgument"> <arguments> <argument name="someArgument" defaultValue="ReplacementPerson" /> diff --git a/dev/tests/verification/_suite/functionalSuiteHooks.xml b/dev/tests/verification/_suite/functionalSuiteHooks.xml index ad6628173..e86ea9590 100644 --- a/dev/tests/verification/_suite/functionalSuiteHooks.xml +++ b/dev/tests/verification/_suite/functionalSuiteHooks.xml @@ -16,10 +16,18 @@ <createData entity="createThis" stepKey="create"> <field key="someKey">dataHere</field> </createData> + <actionGroup ref="actionGroupWithTwoArguments" stepKey="AC"> + <argument name="somePerson" value="simpleData"/> + <argument name="anotherPerson" value="uniqueData"/> + </actionGroup> </before> <after> <amOnPage url="some.url" stepKey="after"/> <deleteData url="deleteThis" stepKey="delete"/> + <actionGroup ref="actionGroupWithTwoArguments" stepKey="AC"> + <argument name="somePerson" value="simpleData"/> + <argument name="anotherPerson" value="uniqueData"/> + </actionGroup> </after> </suite> </suites> diff --git a/etc/di.xml b/etc/di.xml index 5d36928f3..e230bacce 100644 --- a/etc/di.xml +++ b/etc/di.xml @@ -361,6 +361,7 @@ <argument name="idAttributes" xsi:type="array"> <item name="/suites/suite" xsi:type="string">name</item> <item name="/suites/suite/(before|after)/remove" xsi:type="string">keyForRemoval</item> + <item name="/suites/suite/(before|after)/actionGroup/argument" xsi:type="string">name</item> <item name="/suites/suite/(before|after)/(actionGroup|&commonTestActions;)" xsi:type="string">stepKey</item> <item name="/suites/suite/(before|after)/createData/requiredEntity" xsi:type="string">createDataKey</item> <item name="/suites/suite/(before|after)/createData/field" xsi:type="string">key</item> @@ -377,6 +378,7 @@ <argument name="assocArrayAttributes" xsi:type="array"> <item name="/suites/suite" xsi:type="string">name</item> <item name="/suites/suite/(before|after)/remove" xsi:type="string">keyForRemoval</item> + <item name="/suites/suite/(before|after)/actionGroup/argument" xsi:type="string">name</item> <item name="/suites/suite/(before|after)/(actionGroup|&commonTestActions;)" xsi:type="string">stepKey</item> <item name="/suites/suite/(before|after)/createData/requiredEntity" xsi:type="string">createDataKey</item> <item name="/suites/suite/(before|after)/createData/field" xsi:type="string">key</item> From 371f352fc10832043c75d16fa73d1aa979a9bfb3 Mon Sep 17 00:00:00 2001 From: Kevin Kozan <kkozan@magento.com> Date: Wed, 13 Jun 2018 16:54:52 +0300 Subject: [PATCH 058/111] MQE-1043: MFTF force flag should ignore the magento base url even when valid - ModuleResolver now completely ignores trying to get enabledModules if --force is set - Shuffled around exception throwing for faster failure - Fix for MQE-1017 included, exceptions are now more verbose and precise as to when they are thrown. --- .../Util/ModuleResolverTest.php | 109 ++++++++++++++++-- .../Exceptions/TestFrameworkException.php | 12 +- .../Util/ModuleResolver.php | 60 ++++++---- 3 files changed, 147 insertions(+), 34 deletions(-) diff --git a/dev/tests/unit/Magento/FunctionalTestFramework/Util/ModuleResolverTest.php b/dev/tests/unit/Magento/FunctionalTestFramework/Util/ModuleResolverTest.php index 85f09d325..15aa4ad9d 100644 --- a/dev/tests/unit/Magento/FunctionalTestFramework/Util/ModuleResolverTest.php +++ b/dev/tests/unit/Magento/FunctionalTestFramework/Util/ModuleResolverTest.php @@ -9,6 +9,8 @@ use AspectMock\Proxy\Verifier; use AspectMock\Test as AspectMock; +use Magento\FunctionalTestingFramework\Config\MftfApplicationConfig; +use Magento\FunctionalTestingFramework\Exceptions\TestFrameworkException; use Magento\FunctionalTestingFramework\ObjectManager; use Magento\FunctionalTestingFramework\ObjectManagerFactory; use Magento\FunctionalTestingFramework\Util\Logger\LoggingUtil; @@ -54,9 +56,10 @@ public function testGetModulePathsAlreadySet() */ public function testGetModulePathsAggregate() { - $this->setMockResolverClass(false, null, null, null, ["example" . DIRECTORY_SEPARATOR . "paths"]); + $this->mockForceGenerate(false); + $this->setMockResolverClass(false, null, null, null, ["example" => "example" . DIRECTORY_SEPARATOR . "paths"]); $resolver = ModuleResolver::getInstance(); - $this->setMockResolverProperties($resolver, null, null); + $this->setMockResolverProperties($resolver, null, [0 => "Magento_example"]); $this->assertEquals( [ "example" . DIRECTORY_SEPARATOR . "paths", @@ -73,12 +76,13 @@ public function testGetModulePathsAggregate() */ public function testGetModulePathsLocations() { + $this->mockForceGenerate(false); $mockResolver = $this->setMockResolverClass( - false, - null, + true, + [0 => "magento_example"], null, null, - ["example" . DIRECTORY_SEPARATOR . "paths"] + ["example" => "example" . DIRECTORY_SEPARATOR . "paths"] ); $resolver = ModuleResolver::getInstance(); $this->setMockResolverProperties($resolver, null, null); @@ -166,15 +170,78 @@ function ($arg1, $arg2) { } /** - * Validate that getEnabledModules returns correctly with no admin token + * Validate that getEnabledModules errors out when no Admin Token is returned and --force is false * @throws \Exception */ public function testGetModulePathsNoAdminToken() { + // Set --force to false + $this->mockForceGenerate(false); + + // Mock ModuleResolver and $enabledModulesPath $this->setMockResolverClass(false, null, ["example" . DIRECTORY_SEPARATOR . "paths"], []); $resolver = ModuleResolver::getInstance(); $this->setMockResolverProperties($resolver, null, null); - $this->assertEquals(["example" . DIRECTORY_SEPARATOR . "paths"], $resolver->getModulesPath()); + + // Cannot Generate if no --force was passed in and no Admin Token is returned succesfully + $this->expectException(TestFrameworkException::class); + $resolver->getModulesPath(); + } + + /** + * Validates that getAdminToken is not called when --force is enabled + */ + public function testGetAdminTokenNotCalledWhenForce() + { + // Set --force to true + $this->mockForceGenerate(true); + + // Mock ModuleResolver and applyCustomModuleMethods() + $mockResolver = $this->setMockResolverClass(); + $resolver = ModuleResolver::getInstance(); + $this->setMockResolverProperties($resolver, null, null); + $resolver->getModulesPath(); + $mockResolver->verifyNeverInvoked("getAdminToken"); + + // verifyNeverInvoked does not add to assertion count + $this->addToAssertionCount(1); + } + + /** + * Verify the getAdminToken method returns throws an exception if ENV is not fully loaded. + */ + public function testGetAdminTokenWithMissingEnv() + { + // Set --force to true + $this->mockForceGenerate(false); + + // Unset env + unset($_ENV['MAGENTO_ADMIN_USERNAME']); + + // Mock ModuleResolver and applyCustomModuleMethods() + $mockResolver = $this->setMockResolverClass(); + $resolver = ModuleResolver::getInstance(); + + // Expect exception + $this->expectException(TestFrameworkException::class); + $resolver->getModulesPath(); + } + + /** + * Verify the getAdminToken method returns throws an exception if Token was bad. + */ + public function testGetAdminTokenWithBadResponse() + { + // Set --force to true + $this->mockForceGenerate(false); + + // Mock ModuleResolver and applyCustomModuleMethods() + $mockResolver = $this->setMockResolverClass(); + $resolver = ModuleResolver::getInstance(); + + // Expect exception + $this->expectException(TestFrameworkException::class); + $resolver->getModulesPath(); } /** @@ -205,7 +272,7 @@ private function setMockResolverClass( if (isset($mockToken)) { $mockMethods['getAdminToken'] = $mockToken; } - if (isset($mockModules)) { + if (isset($mockGetModules)) { $mockMethods['getEnabledModules'] = $mockGetModules; } if (isset($mockCustomMethods)) { @@ -220,7 +287,7 @@ private function setMockResolverClass( if (isset($mockCustomModules)) { $mockMethods['getCustomModulePaths'] = $mockCustomModules; } - $mockMethods['printMagentoVersionInfo'] = null; +// $mockMethods['printMagentoVersionInfo'] = null; $mockResolver = AspectMock::double( ModuleResolver::class, @@ -260,12 +327,36 @@ private function setMockResolverProperties($instance, $mockPaths = null, $mockMo $property->setValue($instance, $mockBlacklist); } + /** + * Mocks MftfApplicationConfig->forceGenerateEnabled() + * @param $forceGenerate + * @throws \Exception + * @return void + */ + private function mockForceGenerate($forceGenerate) + { + $mockConfig = AspectMock::double( + MftfApplicationConfig::class, + ['forceGenerateEnabled' => $forceGenerate] + ); + $instance = AspectMock::double( + ObjectManager::class, + ['create' => $mockConfig->make(), 'get' => null] + )->make(); + AspectMock::double(ObjectManagerFactory::class, ['getObjectManager' => $instance]); + } + /** * After method functionality * @return void */ protected function tearDown() { + // re set env + if (!isset($_ENV['MAGENTO_ADMIN_USERNAME'])) { + $_ENV['MAGENTO_ADMIN_USERNAME'] = "admin"; + } + AspectMock::clean(); } } diff --git a/src/Magento/FunctionalTestingFramework/Exceptions/TestFrameworkException.php b/src/Magento/FunctionalTestingFramework/Exceptions/TestFrameworkException.php index 9374feebe..3820e25d9 100644 --- a/src/Magento/FunctionalTestingFramework/Exceptions/TestFrameworkException.php +++ b/src/Magento/FunctionalTestingFramework/Exceptions/TestFrameworkException.php @@ -6,6 +6,8 @@ namespace Magento\FunctionalTestingFramework\Exceptions; +use Magento\FunctionalTestingFramework\Util\Logger\LoggingUtil; + /** * Class TestFrameworkException */ @@ -14,9 +16,17 @@ class TestFrameworkException extends \Exception /** * TestFrameworkException constructor. * @param string $message + * @param array $context + * @SuppressWarnings(PHPMD.UnusedLocalVariable) */ - public function __construct($message) + public function __construct($message, $context = []) { + list($childClass, $callingClass) = debug_backtrace(false, 2); + LoggingUtil::getInstance()->getLogger($callingClass['class'])->error( + $message, + $context + ); + parent::__construct($message); } } diff --git a/src/Magento/FunctionalTestingFramework/Util/ModuleResolver.php b/src/Magento/FunctionalTestingFramework/Util/ModuleResolver.php index a32486aba..1b9f3ce2b 100644 --- a/src/Magento/FunctionalTestingFramework/Util/ModuleResolver.php +++ b/src/Magento/FunctionalTestingFramework/Util/ModuleResolver.php @@ -7,6 +7,7 @@ namespace Magento\FunctionalTestingFramework\Util; use Magento\FunctionalTestingFramework\Config\MftfApplicationConfig; +use Magento\FunctionalTestingFramework\Exceptions\TestFrameworkException; use Magento\FunctionalTestingFramework\Util\Logger\LoggingUtil; /** @@ -138,10 +139,6 @@ public function getEnabledModules() } $token = $this->getAdminToken(); - if (!$token || !is_string($token)) { - $this->enabledModules = []; - return $this->enabledModules; - } $url = ConfigSanitizerUtil::sanitizeUrl(getenv('MAGENTO_BASE_URL')) . $this->moduleUrl; @@ -157,10 +154,17 @@ public function getEnabledModules() $response = curl_exec($ch); if (!$response) { - $this->enabledModules = []; - } else { - $this->enabledModules = json_decode($response); + $message = "Could not retrieve Modules from Magento Instance."; + $context = [ + "Admin Module List Url" => $url, + "MAGENTO_ADMIN_USERNAME" => getenv("MAGENTO_ADMIN_USERNAME"), + "MAGENTO_ADMIN_PASSWORD" => getenv("MAGENTO_ADMIN_PASSWORD"), + ]; + throw new TestFrameworkException($message, $context); } + + $this->enabledModules = json_decode($response); + return $this->enabledModules; } @@ -190,25 +194,14 @@ public function getModulesPath() return $this->enabledModulePaths; } - $enabledModules = $this->getEnabledModules(); - if (empty($enabledModules) && !MftfApplicationConfig::getConfig()->forceGenerateEnabled()) { - $errorMsg = 'Could not retrieve enabled modules from provided MAGENTO_BASE_URL ' . - 'please make sure Magento is available at this url'; - LoggingUtil::getInstance()->getLogger(ModuleResolver::class)->error( - $errorMsg, - ['MAGENTO_BASE_URL' => getenv('MAGENTO_BASE_URL')] - ); - trigger_error($errorMsg, E_USER_ERROR); - } - $allModulePaths = $this->aggregateTestModulePaths(); - if (empty($enabledModules)) { + if (MftfApplicationConfig::getConfig()->forceGenerateEnabled()) { $this->enabledModulePaths = $this->applyCustomModuleMethods($allModulePaths); return $this->enabledModulePaths; } - $enabledModules = array_merge($enabledModules, $this->getModuleWhitelist()); + $enabledModules = array_merge($this->getEnabledModules(), $this->getModuleWhitelist()); $enabledDirectoryPaths = $this->getEnabledDirectoryPaths($enabledModules, $allModulePaths); $this->enabledModulePaths = $this->applyCustomModuleMethods($enabledDirectoryPaths); @@ -325,9 +318,15 @@ private function getEnabledDirectoryPaths($enabledModules, $allModulePaths) { $enabledDirectoryPaths = []; foreach ($enabledModules as $magentoModuleName) { - $moduleShortName = explode('_', $magentoModuleName)[1]; + // Magento_Backend -> Backend or DevDocs -> DevDocs (if whitelisted has no underscore) + $moduleShortName = explode('_', $magentoModuleName)[1] ?? $magentoModuleName; if (!isset($this->knownDirectories[$moduleShortName]) && !isset($allModulePaths[$moduleShortName])) { continue; + } elseif (isset($this->knownDirectories[$moduleShortName]) && !isset($allModulePaths[$moduleShortName])) { + LoggingUtil::getInstance()->getLogger(ModuleResolver::class)->warn( + "Known directory could not match to an existing path.", + ['knownDirectory' => $moduleShortName] + ); } else { $enabledDirectoryPaths[$moduleShortName] = $allModulePaths[$moduleShortName]; } @@ -342,7 +341,6 @@ private function getEnabledDirectoryPaths($enabledModules, $allModulePaths) */ private function printMagentoVersionInfo() { - if (MftfApplicationConfig::getConfig()->forceGenerateEnabled()) { return; } @@ -377,7 +375,13 @@ protected function getAdminToken() $login = $_ENV['MAGENTO_ADMIN_USERNAME'] ?? null; $password = $_ENV['MAGENTO_ADMIN_PASSWORD'] ?? null; if (!$login || !$password || !isset($_ENV['MAGENTO_BASE_URL'])) { - return false; + $message = "Cannot retrieve API token without credentials and base url, please fill out .env."; + $context = [ + "MAGENTO_BASE_URL" => getenv("MAGENTO_BASE_URL"), + "MAGENTO_ADMIN_USERNAME" => getenv("MAGENTO_ADMIN_USERNAME"), + "MAGENTO_ADMIN_PASSWORD" => getenv("MAGENTO_ADMIN_PASSWORD"), + ]; + throw new TestFrameworkException($message, $context); } $url = ConfigSanitizerUtil::sanitizeUrl($_ENV['MAGENTO_BASE_URL']) . $this->adminTokenUrl; @@ -398,9 +402,17 @@ protected function getAdminToken() curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); $response = curl_exec($ch); + if (!$response) { - return $response; + $message = "Could not retrieve API token from Magento Instance."; + $context = [ + "Admin Integration Token Url" => $url, + "MAGENTO_ADMIN_USERNAME" => getenv("MAGENTO_ADMIN_USERNAME"), + "MAGENTO_ADMIN_PASSWORD" => getenv("MAGENTO_ADMIN_PASSWORD"), + ]; + throw new TestFrameworkException($message, $context); } + return json_decode($response); } From 5bdd3d2257eefa125be9bcec474a685297d688b9 Mon Sep 17 00:00:00 2001 From: Ian Meron <imeron@magento.com> Date: Wed, 13 Jun 2018 09:28:27 -0500 Subject: [PATCH 059/111] MQE-1059: When a single test is specified all suites should not generate - add logic to flatten assoc array and wrap suite generation in conditional logic --- .../Config/Dom/ArrayNodeConfig.php | 65 +++++++++++++++++++ .../Console/GenerateTestsCommand.php | 5 +- .../Handlers/DataObjectHandler.php | 2 +- 3 files changed, 70 insertions(+), 2 deletions(-) diff --git a/src/Magento/FunctionalTestingFramework/Config/Dom/ArrayNodeConfig.php b/src/Magento/FunctionalTestingFramework/Config/Dom/ArrayNodeConfig.php index eaa216649..8bb06376a 100644 --- a/src/Magento/FunctionalTestingFramework/Config/Dom/ArrayNodeConfig.php +++ b/src/Magento/FunctionalTestingFramework/Config/Dom/ArrayNodeConfig.php @@ -24,6 +24,13 @@ class ArrayNodeConfig */ private $assocArrays = []; + /** + * Flat array of expanded patterns for matching xpath + * + * @var array + */ + private $flatAssocArray = []; + /** * Format: array('/numeric/array/path', ...) * @@ -44,6 +51,7 @@ public function __construct( ) { $this->nodePathMatcher = $nodePathMatcher; $this->assocArrays = $assocArrayAttributes; + $this->flatAssocArray = $this->flattenToAssocKeyAttributes($assocArrayAttributes); $this->numericArrays = $numericArrays; } @@ -71,6 +79,10 @@ public function isNumericArray($nodeXpath) */ public function getAssocArrayKeyAttribute($nodeXpath) { + if (array_key_exists($nodeXpath, $this->flatAssocArray)) { + return $this->flatAssocArray[$nodeXpath]; + } + foreach ($this->assocArrays as $pathPattern => $keyAttribute) { if ($this->nodePathMatcher->match($pathPattern, $nodeXpath)) { return $keyAttribute; @@ -78,4 +90,57 @@ public function getAssocArrayKeyAttribute($nodeXpath) } return null; } + + /** + * Function which takes a patterned list of xpath matchers and flattens to a single level array for + * performance improvement + * + * @param array $assocArrayAttributes + * @return array + */ + private function flattenToAssocKeyAttributes($assocArrayAttributes) + { + $finalPatterns = []; + foreach ($assocArrayAttributes as $pattern => $key) { + $vars = explode("/", ltrim($pattern, "/")); + $stringPatterns = [""]; + foreach ($vars as $var) { + if (strstr($var, "|")) { + $repOpen = str_replace("(", "", $var); + $repClosed = str_replace(")", "", $repOpen); + $nestedPatterns = explode("|", $repClosed); + $stringPatterns = $this->mergeStrings($stringPatterns, $nestedPatterns); + continue; + } + + // append this path to all of the paths that currently exist + array_walk($stringPatterns, function (&$value, $key) use ($var) { + $value .= "/" . $var; + }); + } + + $finalPatterns = array_merge($finalPatterns, array_fill_keys($stringPatterns, $key)); + } + + return $finalPatterns; + } + + /** + * Takes 2 arrays and appends all string in the second array to each entry in the first. + * + * @param string[] $parentStrings + * @param string[] $childStrings + * @return array + */ + private function mergeStrings($parentStrings, $childStrings) + { + $result = []; + foreach ($parentStrings as $pString) { + foreach ($childStrings as $cString) { + $result[] = $pString . "/" . $cString; + } + } + + return $result; + } } diff --git a/src/Magento/FunctionalTestingFramework/Console/GenerateTestsCommand.php b/src/Magento/FunctionalTestingFramework/Console/GenerateTestsCommand.php index 119fe48f9..d9ef5dea5 100644 --- a/src/Magento/FunctionalTestingFramework/Console/GenerateTestsCommand.php +++ b/src/Magento/FunctionalTestingFramework/Console/GenerateTestsCommand.php @@ -77,7 +77,10 @@ protected function execute(InputInterface $input, OutputInterface $output) $testManifest->createTestGroups($time); } - SuiteGenerator::getInstance()->generateAllSuites($testManifest); + if (empty($tests)) { + SuiteGenerator::getInstance()->generateAllSuites($testManifest); + } + $testManifest->generate(); $output->writeln("Generate Tests Command Run"); diff --git a/src/Magento/FunctionalTestingFramework/DataGenerator/Handlers/DataObjectHandler.php b/src/Magento/FunctionalTestingFramework/DataGenerator/Handlers/DataObjectHandler.php index 5dc9293c2..9ab14e5ff 100644 --- a/src/Magento/FunctionalTestingFramework/DataGenerator/Handlers/DataObjectHandler.php +++ b/src/Magento/FunctionalTestingFramework/DataGenerator/Handlers/DataObjectHandler.php @@ -128,7 +128,7 @@ private function processParserOutput($parserOutput) throw new XmlException(sprintf(self::DATA_NAME_ERROR_MSG, $name)); } - $type = $rawEntity[self::_TYPE]; + $type = $rawEntity[self::_TYPE] ?? null; $data = []; $linkedEntities = []; $uniquenessData = []; From 320e5d38481d476608204f4e1e6165fb0894b288 Mon Sep 17 00:00:00 2001 From: Alex Calandra <acalandra@magento.com> Date: Wed, 13 Jun 2018 09:53:19 -0500 Subject: [PATCH 060/111] MQE-1017: Better Error Messaging When Non-Whitespace Characters Are Outside XML Elements - Changed default verification tests to use debug turned off --- dev/tests/_bootstrap.php | 2 +- .../verification/Tests/SchemaValidationTest.php | 12 ++++++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/dev/tests/_bootstrap.php b/dev/tests/_bootstrap.php index 84cf32264..0adfc2e74 100644 --- a/dev/tests/_bootstrap.php +++ b/dev/tests/_bootstrap.php @@ -32,7 +32,7 @@ true, \Magento\FunctionalTestingFramework\Config\MftfApplicationConfig::GENERATION_PHASE, true, - true + false ); // Load needed framework env params diff --git a/dev/tests/verification/Tests/SchemaValidationTest.php b/dev/tests/verification/Tests/SchemaValidationTest.php index 7d247693e..86828e9a5 100644 --- a/dev/tests/verification/Tests/SchemaValidationTest.php +++ b/dev/tests/verification/Tests/SchemaValidationTest.php @@ -5,7 +5,9 @@ */ namespace tests\verification\Tests; +use Magento\FunctionalTestingFramework\Config\MftfApplicationConfig; use tests\util\MftfTestCase; +use AspectMock\Test as AspectMock; class SchemaValidationTest extends MftfTestCase { @@ -17,6 +19,7 @@ class SchemaValidationTest extends MftfTestCase */ public function testInvalidTestSchema() { + AspectMock::double(MftfApplicationConfig::class, ['debugEnabled' => true]); $testFile = ['testFile.xml' => "<tests><test name='testName'><annotations>a</annotations></test></tests>"]; $expectedError = TESTS_MODULE_PATH . DIRECTORY_SEPARATOR . @@ -27,4 +30,13 @@ public function testInvalidTestSchema() "testFile.xml"; $this->validateSchemaErrorWithTest($testFile, 'Test', $expectedError); } + + /** + * After method functionality + * @return void + */ + protected function tearDown() + { + AspectMock::clean(); + } } From 3ff6d16d63ba9b338227fc0c1562f3f5b7c683f6 Mon Sep 17 00:00:00 2001 From: Kevin Kozan <kkozan@magento.com> Date: Wed, 13 Jun 2018 18:14:31 +0300 Subject: [PATCH 061/111] MQE-1038: bin/mftf commands which exceed 60 seconds timeout - added idletimeout/timeout to all $process calls in bin/mftf commands --- .../FunctionalTestingFramework/Console/BuildProjectCommand.php | 2 ++ .../FunctionalTestingFramework/Console/RunTestCommand.php | 3 ++- .../FunctionalTestingFramework/Console/RunTestGroupCommand.php | 2 ++ 3 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src/Magento/FunctionalTestingFramework/Console/BuildProjectCommand.php b/src/Magento/FunctionalTestingFramework/Console/BuildProjectCommand.php index ea5c5795f..66378da8d 100644 --- a/src/Magento/FunctionalTestingFramework/Console/BuildProjectCommand.php +++ b/src/Magento/FunctionalTestingFramework/Console/BuildProjectCommand.php @@ -76,6 +76,8 @@ protected function execute(InputInterface $input, OutputInterface $output) $codeceptBuildCommand = realpath(PROJECT_ROOT . '/vendor/bin/codecept') . ' build'; $process = new Process($codeceptBuildCommand); $process->setWorkingDirectory(TESTS_BP); + $process->setIdleTimeout(600); + $process->setTimeout(0); $process->run( function ($type, $buffer) use ($output) { if ($output->isVerbose()) { diff --git a/src/Magento/FunctionalTestingFramework/Console/RunTestCommand.php b/src/Magento/FunctionalTestingFramework/Console/RunTestCommand.php index 6041d0be5..ed755d705 100644 --- a/src/Magento/FunctionalTestingFramework/Console/RunTestCommand.php +++ b/src/Magento/FunctionalTestingFramework/Console/RunTestCommand.php @@ -63,11 +63,12 @@ protected function execute(InputInterface $input, OutputInterface $output) $process = new Process($codeceptionCommand); $process->setWorkingDirectory(TESTS_BP); + $process->setIdleTimeout(600); + $process->setTimeout(0); $process->run( function ($type, $buffer) use ($output) { $output->write($buffer); } ); } - } diff --git a/src/Magento/FunctionalTestingFramework/Console/RunTestGroupCommand.php b/src/Magento/FunctionalTestingFramework/Console/RunTestGroupCommand.php index c0e5d99b7..1fda1555d 100644 --- a/src/Magento/FunctionalTestingFramework/Console/RunTestGroupCommand.php +++ b/src/Magento/FunctionalTestingFramework/Console/RunTestGroupCommand.php @@ -68,6 +68,8 @@ protected function execute(InputInterface $input, OutputInterface $output) $process = new Process($codeceptionCommand); $process->setWorkingDirectory(TESTS_BP); + $process->setIdleTimeout(600); + $process->setTimeout(0); $process->run( function ($type, $buffer) use ($output) { $output->write($buffer); From 5307abbcbd70082313d93e8a352688527e4bf701 Mon Sep 17 00:00:00 2001 From: Kevin Kozan <kkozan@magento.com> Date: Wed, 13 Jun 2018 19:15:40 +0300 Subject: [PATCH 062/111] MQE-1016: Variable Substitution Does Not Work For Command Attribute In MagentoCLI Action - Enabled MagentoCLI's command attribtue to have data substitution. --- dev/tests/verification/Resources/DataReplacementTest.txt | 2 ++ .../verification/TestModule/Test/DataReplacementTest.xml | 1 + .../Test/Objects/ActionObject.php | 6 ++++-- .../FunctionalTestingFramework/Util/TestGenerator.php | 4 ++-- 4 files changed, 9 insertions(+), 4 deletions(-) diff --git a/dev/tests/verification/Resources/DataReplacementTest.txt b/dev/tests/verification/Resources/DataReplacementTest.txt index 07b733ff4..57f94886d 100644 --- a/dev/tests/verification/Resources/DataReplacementTest.txt +++ b/dev/tests/verification/Resources/DataReplacementTest.txt @@ -51,5 +51,7 @@ class DataReplacementTestCest $I->searchAndMultiSelectOption("#selector", [msq("uniqueData") . "John", "Doe" . msq("uniqueData")]); $I->selectMultipleOptions("#Doe" . msq("uniqueData"), "#element", [msq("uniqueData") . "John", "Doe" . msq("uniqueData")]); $I->fillField(".selector", "0"); + $insertCommand = $I->magentoCLI("do something Doe" . msq("uniqueData") . " with uniqueness"); + $I->comment($insertCommand); } } diff --git a/dev/tests/verification/TestModule/Test/DataReplacementTest.xml b/dev/tests/verification/TestModule/Test/DataReplacementTest.xml index f12c70b26..df2bb5789 100644 --- a/dev/tests/verification/TestModule/Test/DataReplacementTest.xml +++ b/dev/tests/verification/TestModule/Test/DataReplacementTest.xml @@ -37,5 +37,6 @@ </selectMultipleOptions> <fillField stepKey="insertZero" selector=".selector" userInput="{{simpleData.favoriteIndex}}"/> + <magentoCLI stepKey="insertCommand" command="do something {{uniqueData.lastname}} with uniqueness"/> </test> </tests> \ No newline at end of file diff --git a/src/Magento/FunctionalTestingFramework/Test/Objects/ActionObject.php b/src/Magento/FunctionalTestingFramework/Test/Objects/ActionObject.php index a7e3117da..1fcff34db 100644 --- a/src/Magento/FunctionalTestingFramework/Test/Objects/ActionObject.php +++ b/src/Magento/FunctionalTestingFramework/Test/Objects/ActionObject.php @@ -31,7 +31,8 @@ class ActionObject "x", "y", "expectedResult", - "actualResult" + "actualResult", + "command" ]; const SELECTOR_ENABLED_ATTRIBUTES = [ 'selector', @@ -40,7 +41,8 @@ class ActionObject "selector2", "function", 'filterSelector', - 'optionSelector' + 'optionSelector', + "command" ]; const OLD_ASSERTION_ATTRIBUTES = ["expected", "expectedType", "actual", "actualType"]; const ASSERTION_ATTRIBUTES = ["expectedResult" => "expected", "actualResult" => "actual"]; diff --git a/src/Magento/FunctionalTestingFramework/Util/TestGenerator.php b/src/Magento/FunctionalTestingFramework/Util/TestGenerator.php index c55cfa916..00203a7e7 100644 --- a/src/Magento/FunctionalTestingFramework/Util/TestGenerator.php +++ b/src/Magento/FunctionalTestingFramework/Util/TestGenerator.php @@ -500,7 +500,7 @@ public function generateStepsPhp($actionObjects, $hookObject = false, $actor = " $this->validateXmlAttributesMutuallyExclusive($stepKey, $actionObject->getType(), $customActionAttributes); if (isset($customActionAttributes['command'])) { - $command = $customActionAttributes['command']; + $command = $this->addUniquenessFunctionCall($customActionAttributes['command']); } if (isset($customActionAttributes['attribute'])) { @@ -1217,7 +1217,7 @@ public function generateStepsPhp($actionObjects, $hookObject = false, $actor = " $stepKey, $actor, $actionObject, - $this->wrapWithDoubleQuotes($command) + $command ); $testSteps .= sprintf( "\t\t$%s->comment(\$%s);\n", From 914ebe1c3bc012d344ba1aec0a85b9c0a1814770 Mon Sep 17 00:00:00 2001 From: Alex Calandra <acalandra@magento.com> Date: Wed, 13 Jun 2018 16:13:51 -0500 Subject: [PATCH 063/111] MQE-1021: Empty Action StepKey Attribute Issues - Added filename information for action groups actions as well --- .../Util/ActionGroupObjectExtractorTest.php | 68 +++++++++++++++++++ .../Test/Util/ActionGroupObjectExtractor.php | 6 +- 2 files changed, 73 insertions(+), 1 deletion(-) create mode 100644 dev/tests/unit/Magento/FunctionalTestFramework/Test/Util/ActionGroupObjectExtractorTest.php diff --git a/dev/tests/unit/Magento/FunctionalTestFramework/Test/Util/ActionGroupObjectExtractorTest.php b/dev/tests/unit/Magento/FunctionalTestFramework/Test/Util/ActionGroupObjectExtractorTest.php new file mode 100644 index 000000000..25e53c570 --- /dev/null +++ b/dev/tests/unit/Magento/FunctionalTestFramework/Test/Util/ActionGroupObjectExtractorTest.php @@ -0,0 +1,68 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +namespace tests\unit\Magento\FunctionalTestFramework\Test\Util; + +use Magento\FunctionalTestingFramework\Test\Util\ActionGroupObjectExtractor; +use Magento\FunctionalTestingFramework\Util\MagentoTestCase; +use tests\unit\Util\TestLoggingUtil; + +class ActionGroupObjectExtractorTest extends MagentoTestCase +{ + /** @var ActionGroupObjectExtractor */ + private $testActionGroupObjectExtractor; + + /** + * Setup method + */ + public function setUp() + { + $this->testActionGroupObjectExtractor = new ActionGroupObjectExtractor(); + TestLoggingUtil::getInstance()->setMockLoggingUtil(); + } + + /** + * Tests basic action object extraction with an empty stepKey + */ + public function testEmptyStepKey() + { + $this->expectExceptionMessage("StepKeys cannot be empty. Action='sampleAction' in filename.xml"); + $this->testActionGroupObjectExtractor->extractActionGroup($this->createBasicActionObjectArray("")); + } + + /** + * Utility function to return mock parser output for testing extraction into ActionObjects. + * + * @param string $stepKey + * @param string $actionGroup + * @param string $filename + * @return array + */ + private function createBasicActionObjectArray( + $stepKey = 'testAction1', + $actionGroup = "actionGroup", + $filename = "filename.xml" + ) { + $baseArray = [ + 'nodeName' => 'actionGroup', + 'name' => $actionGroup, + 'filename' => $filename, + $stepKey => [ + "nodeName" => "sampleAction", + "stepKey" => $stepKey, + "someAttribute" => "someAttributeValue" + ] + ]; + return $baseArray; + } + + /** + * clean up function runs after all tests + */ + public static function tearDownAfterClass() + { + TestLoggingUtil::getInstance()->clearMockLoggingUtil(); + } +} diff --git a/src/Magento/FunctionalTestingFramework/Test/Util/ActionGroupObjectExtractor.php b/src/Magento/FunctionalTestingFramework/Test/Util/ActionGroupObjectExtractor.php index fd8093358..bedd6575e 100644 --- a/src/Magento/FunctionalTestingFramework/Test/Util/ActionGroupObjectExtractor.php +++ b/src/Magento/FunctionalTestingFramework/Test/Util/ActionGroupObjectExtractor.php @@ -62,7 +62,11 @@ public function extractActionGroup($actionGroupData) ); // TODO filename is now available to the ActionGroupObject, integrate this into debug and error statements - $actions = $this->actionObjectExtractor->extractActions($actionData); + try { + $actions = $this->actionObjectExtractor->extractActions($actionData); + } catch (\Exception $error) { + throw new XmlException($error->getMessage() . " in " . $actionGroupData[self::FILENAME]); + } if (array_key_exists(self::ACTION_GROUP_ARGUMENTS, $actionGroupData)) { $arguments = $this->extractArguments($actionGroupData[self::ACTION_GROUP_ARGUMENTS]); From 35257580effb9801a23d2785be806dabf079eb40 Mon Sep 17 00:00:00 2001 From: Alex Calandra <acalandra@magento.com> Date: Wed, 13 Jun 2018 16:17:11 -0500 Subject: [PATCH 064/111] MQE-1021: Empty Action StepKey Attribute Issues - Made error message slightly more specific --- .../Test/Util/ActionGroupObjectExtractorTest.php | 4 +++- .../Test/Util/ActionGroupObjectExtractor.php | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/dev/tests/unit/Magento/FunctionalTestFramework/Test/Util/ActionGroupObjectExtractorTest.php b/dev/tests/unit/Magento/FunctionalTestFramework/Test/Util/ActionGroupObjectExtractorTest.php index 25e53c570..47b797c1c 100644 --- a/dev/tests/unit/Magento/FunctionalTestFramework/Test/Util/ActionGroupObjectExtractorTest.php +++ b/dev/tests/unit/Magento/FunctionalTestFramework/Test/Util/ActionGroupObjectExtractorTest.php @@ -28,7 +28,9 @@ public function setUp() */ public function testEmptyStepKey() { - $this->expectExceptionMessage("StepKeys cannot be empty. Action='sampleAction' in filename.xml"); + $this->expectExceptionMessage( + "StepKeys cannot be empty. Action='sampleAction' in Action Group filename.xml" + ); $this->testActionGroupObjectExtractor->extractActionGroup($this->createBasicActionObjectArray("")); } diff --git a/src/Magento/FunctionalTestingFramework/Test/Util/ActionGroupObjectExtractor.php b/src/Magento/FunctionalTestingFramework/Test/Util/ActionGroupObjectExtractor.php index bedd6575e..04f739af6 100644 --- a/src/Magento/FunctionalTestingFramework/Test/Util/ActionGroupObjectExtractor.php +++ b/src/Magento/FunctionalTestingFramework/Test/Util/ActionGroupObjectExtractor.php @@ -65,7 +65,7 @@ public function extractActionGroup($actionGroupData) try { $actions = $this->actionObjectExtractor->extractActions($actionData); } catch (\Exception $error) { - throw new XmlException($error->getMessage() . " in " . $actionGroupData[self::FILENAME]); + throw new XmlException($error->getMessage() . " in Action Group " . $actionGroupData[self::FILENAME]); } if (array_key_exists(self::ACTION_GROUP_ARGUMENTS, $actionGroupData)) { From 73beb17793975b4a38e5183f985346e9d3f95f11 Mon Sep 17 00:00:00 2001 From: Ian Meron <imeron@magento.com> Date: Mon, 18 Jun 2018 11:08:03 -0500 Subject: [PATCH 065/111] MQE-1006: Handling secure/sensitive data in MFTF test - add credential manager - add .credentials example file - update TestGenerator to reference new cred manager --- .../ActionGroupContainsStepKeyInArgText.txt | 1 + .../ActionGroupMergedViaInsertAfter.txt | 1 + .../ActionGroupMergedViaInsertBefore.txt | 1 + .../Resources/ActionGroupToExtend.txt | 1 + .../Resources/ActionGroupUsingCreateData.txt | 1 + .../ActionGroupUsingNestedArgument.txt | 1 + .../ActionGroupWithDataOverrideTest.txt | 1 + .../Resources/ActionGroupWithDataTest.txt | 1 + ...hDefaultArgumentAndStringSelectorParam.txt | 1 + ...eParameterSelectorsFromDefaultArgument.txt | 1 + .../Resources/ActionGroupWithNoArguments.txt | 1 + .../ActionGroupWithNoDefaultTest.txt | 1 + ...thPassedArgumentAndStringSelectorParam.txt | 1 + .../ActionGroupWithPersistedData.txt | 1 + ...WithSimpleDataUsageFromDefaultArgument.txt | 1 + ...pWithSimpleDataUsageFromPassedArgument.txt | 1 + ...leParameterSelectorFromDefaultArgument.txt | 1 + ...gleParameterSelectorFromPassedArgument.txt | 1 + .../ActionGroupWithStepKeyReferences.txt | 1 + .../ActionGroupWithTopLevelPersistedData.txt | 1 + .../ArgumentWithSameNameAsElement.txt | 1 + .../verification/Resources/AssertTest.txt | 1 + .../Resources/BasicActionGroupTest.txt | 1 + .../Resources/BasicFunctionalTest.txt | 1 + .../verification/Resources/BasicMergeTest.txt | 1 + .../Resources/CharacterReplacementTest.txt | 1 + .../Resources/ChildExtendedTestAddHooks.txt | 1 + .../Resources/ChildExtendedTestMerging.txt | 1 + .../Resources/ChildExtendedTestNoParent.txt | 1 + .../ChildExtendedTestRemoveAction.txt | 1 + .../ChildExtendedTestRemoveHookAction.txt | 1 + .../Resources/ChildExtendedTestReplace.txt | 1 + .../ChildExtendedTestReplaceHook.txt | 1 + .../Resources/DataReplacementTest.txt | 1 + .../Resources/ExtendParentDataTest.txt | 1 + .../Resources/ExtendedActionGroup.txt | 1 + .../Resources/ExtendedParameterArrayTest.txt | 1 + .../Resources/ExtendedRemoveActionGroup.txt | 1 + .../Resources/HookActionsTest.txt | 1 + .../Resources/LocatorFunctionTest.txt | 1 + .../Resources/MergeMassViaInsertAfter.txt | 1 + .../Resources/MergeMassViaInsertBefore.txt | 1 + .../Resources/MergedActionGroupTest.txt | 1 + .../Resources/MergedReferencesTest.txt | 1 + .../Resources/MultipleActionGroupsTest.txt | 1 + .../Resources/PageReplacementTest.txt | 1 + .../Resources/ParameterArrayTest.txt | 1 + .../Resources/ParentExtendedTest.txt | 1 + .../PersistedAndXmlEntityArguments.txt | 1 + .../Resources/PersistedReplacementTest.txt | 2 + .../Resources/PersistenceCustomFieldsTest.txt | 1 + .../Resources/SectionReplacementTest.txt | 1 + .../Test/PersistedReplacementTest.xml | 1 + etc/config/.credentials.example | 75 ++++++++++++ .../Console/BuildProjectCommand.php | 9 ++ .../Handlers/CredentialStore.php | 110 ++++++++++++++++++ .../Test/Objects/ActionObject.php | 10 +- .../Util/Env/EnvProcessor.php | 6 +- .../Util/TestGenerator.php | 42 +++++-- 59 files changed, 290 insertions(+), 16 deletions(-) create mode 100644 etc/config/.credentials.example create mode 100644 src/Magento/FunctionalTestingFramework/DataGenerator/Handlers/CredentialStore.php diff --git a/dev/tests/verification/Resources/ActionGroupContainsStepKeyInArgText.txt b/dev/tests/verification/Resources/ActionGroupContainsStepKeyInArgText.txt index c41170b5c..60b645cfa 100644 --- a/dev/tests/verification/Resources/ActionGroupContainsStepKeyInArgText.txt +++ b/dev/tests/verification/Resources/ActionGroupContainsStepKeyInArgText.txt @@ -5,6 +5,7 @@ use Magento\FunctionalTestingFramework\AcceptanceTester; use Magento\FunctionalTestingFramework\DataGenerator\Handlers\DataObjectHandler; use Magento\FunctionalTestingFramework\DataGenerator\Persist\DataPersistenceHandler; use Magento\FunctionalTestingFramework\DataGenerator\Objects\EntityDataObject; +use Magento\FunctionalTestingFramework\DataGenerator\Handlers\CredentialStore; use \Codeception\Util\Locator; use Yandex\Allure\Adapter\Annotation\Features; use Yandex\Allure\Adapter\Annotation\Stories; diff --git a/dev/tests/verification/Resources/ActionGroupMergedViaInsertAfter.txt b/dev/tests/verification/Resources/ActionGroupMergedViaInsertAfter.txt index a62548ccc..ddee76f82 100644 --- a/dev/tests/verification/Resources/ActionGroupMergedViaInsertAfter.txt +++ b/dev/tests/verification/Resources/ActionGroupMergedViaInsertAfter.txt @@ -5,6 +5,7 @@ use Magento\FunctionalTestingFramework\AcceptanceTester; use Magento\FunctionalTestingFramework\DataGenerator\Handlers\DataObjectHandler; use Magento\FunctionalTestingFramework\DataGenerator\Persist\DataPersistenceHandler; use Magento\FunctionalTestingFramework\DataGenerator\Objects\EntityDataObject; +use Magento\FunctionalTestingFramework\DataGenerator\Handlers\CredentialStore; use \Codeception\Util\Locator; use Yandex\Allure\Adapter\Annotation\Features; use Yandex\Allure\Adapter\Annotation\Stories; diff --git a/dev/tests/verification/Resources/ActionGroupMergedViaInsertBefore.txt b/dev/tests/verification/Resources/ActionGroupMergedViaInsertBefore.txt index 504ca8120..4121a6d48 100644 --- a/dev/tests/verification/Resources/ActionGroupMergedViaInsertBefore.txt +++ b/dev/tests/verification/Resources/ActionGroupMergedViaInsertBefore.txt @@ -5,6 +5,7 @@ use Magento\FunctionalTestingFramework\AcceptanceTester; use Magento\FunctionalTestingFramework\DataGenerator\Handlers\DataObjectHandler; use Magento\FunctionalTestingFramework\DataGenerator\Persist\DataPersistenceHandler; use Magento\FunctionalTestingFramework\DataGenerator\Objects\EntityDataObject; +use Magento\FunctionalTestingFramework\DataGenerator\Handlers\CredentialStore; use \Codeception\Util\Locator; use Yandex\Allure\Adapter\Annotation\Features; use Yandex\Allure\Adapter\Annotation\Stories; diff --git a/dev/tests/verification/Resources/ActionGroupToExtend.txt b/dev/tests/verification/Resources/ActionGroupToExtend.txt index bd077636d..fb8a9fd60 100644 --- a/dev/tests/verification/Resources/ActionGroupToExtend.txt +++ b/dev/tests/verification/Resources/ActionGroupToExtend.txt @@ -5,6 +5,7 @@ use Magento\FunctionalTestingFramework\AcceptanceTester; use Magento\FunctionalTestingFramework\DataGenerator\Handlers\DataObjectHandler; use Magento\FunctionalTestingFramework\DataGenerator\Persist\DataPersistenceHandler; use Magento\FunctionalTestingFramework\DataGenerator\Objects\EntityDataObject; +use Magento\FunctionalTestingFramework\DataGenerator\Handlers\CredentialStore; use \Codeception\Util\Locator; use Yandex\Allure\Adapter\Annotation\Features; use Yandex\Allure\Adapter\Annotation\Stories; diff --git a/dev/tests/verification/Resources/ActionGroupUsingCreateData.txt b/dev/tests/verification/Resources/ActionGroupUsingCreateData.txt index 2acf9e297..41953aa3f 100644 --- a/dev/tests/verification/Resources/ActionGroupUsingCreateData.txt +++ b/dev/tests/verification/Resources/ActionGroupUsingCreateData.txt @@ -5,6 +5,7 @@ use Magento\FunctionalTestingFramework\AcceptanceTester; use Magento\FunctionalTestingFramework\DataGenerator\Handlers\DataObjectHandler; use Magento\FunctionalTestingFramework\DataGenerator\Persist\DataPersistenceHandler; use Magento\FunctionalTestingFramework\DataGenerator\Objects\EntityDataObject; +use Magento\FunctionalTestingFramework\DataGenerator\Handlers\CredentialStore; use \Codeception\Util\Locator; use Yandex\Allure\Adapter\Annotation\Features; use Yandex\Allure\Adapter\Annotation\Stories; diff --git a/dev/tests/verification/Resources/ActionGroupUsingNestedArgument.txt b/dev/tests/verification/Resources/ActionGroupUsingNestedArgument.txt index a36c37e7a..674de1afc 100644 --- a/dev/tests/verification/Resources/ActionGroupUsingNestedArgument.txt +++ b/dev/tests/verification/Resources/ActionGroupUsingNestedArgument.txt @@ -5,6 +5,7 @@ use Magento\FunctionalTestingFramework\AcceptanceTester; use Magento\FunctionalTestingFramework\DataGenerator\Handlers\DataObjectHandler; use Magento\FunctionalTestingFramework\DataGenerator\Persist\DataPersistenceHandler; use Magento\FunctionalTestingFramework\DataGenerator\Objects\EntityDataObject; +use Magento\FunctionalTestingFramework\DataGenerator\Handlers\CredentialStore; use \Codeception\Util\Locator; use Yandex\Allure\Adapter\Annotation\Features; use Yandex\Allure\Adapter\Annotation\Stories; diff --git a/dev/tests/verification/Resources/ActionGroupWithDataOverrideTest.txt b/dev/tests/verification/Resources/ActionGroupWithDataOverrideTest.txt index c939f421e..6815d318b 100644 --- a/dev/tests/verification/Resources/ActionGroupWithDataOverrideTest.txt +++ b/dev/tests/verification/Resources/ActionGroupWithDataOverrideTest.txt @@ -5,6 +5,7 @@ use Magento\FunctionalTestingFramework\AcceptanceTester; use Magento\FunctionalTestingFramework\DataGenerator\Handlers\DataObjectHandler; use Magento\FunctionalTestingFramework\DataGenerator\Persist\DataPersistenceHandler; use Magento\FunctionalTestingFramework\DataGenerator\Objects\EntityDataObject; +use Magento\FunctionalTestingFramework\DataGenerator\Handlers\CredentialStore; use \Codeception\Util\Locator; use Yandex\Allure\Adapter\Annotation\Features; use Yandex\Allure\Adapter\Annotation\Stories; diff --git a/dev/tests/verification/Resources/ActionGroupWithDataTest.txt b/dev/tests/verification/Resources/ActionGroupWithDataTest.txt index d11eff9a8..e4b79cf73 100644 --- a/dev/tests/verification/Resources/ActionGroupWithDataTest.txt +++ b/dev/tests/verification/Resources/ActionGroupWithDataTest.txt @@ -5,6 +5,7 @@ use Magento\FunctionalTestingFramework\AcceptanceTester; use Magento\FunctionalTestingFramework\DataGenerator\Handlers\DataObjectHandler; use Magento\FunctionalTestingFramework\DataGenerator\Persist\DataPersistenceHandler; use Magento\FunctionalTestingFramework\DataGenerator\Objects\EntityDataObject; +use Magento\FunctionalTestingFramework\DataGenerator\Handlers\CredentialStore; use \Codeception\Util\Locator; use Yandex\Allure\Adapter\Annotation\Features; use Yandex\Allure\Adapter\Annotation\Stories; diff --git a/dev/tests/verification/Resources/ActionGroupWithDefaultArgumentAndStringSelectorParam.txt b/dev/tests/verification/Resources/ActionGroupWithDefaultArgumentAndStringSelectorParam.txt index 421bb4af7..79c95b0df 100644 --- a/dev/tests/verification/Resources/ActionGroupWithDefaultArgumentAndStringSelectorParam.txt +++ b/dev/tests/verification/Resources/ActionGroupWithDefaultArgumentAndStringSelectorParam.txt @@ -5,6 +5,7 @@ use Magento\FunctionalTestingFramework\AcceptanceTester; use Magento\FunctionalTestingFramework\DataGenerator\Handlers\DataObjectHandler; use Magento\FunctionalTestingFramework\DataGenerator\Persist\DataPersistenceHandler; use Magento\FunctionalTestingFramework\DataGenerator\Objects\EntityDataObject; +use Magento\FunctionalTestingFramework\DataGenerator\Handlers\CredentialStore; use \Codeception\Util\Locator; use Yandex\Allure\Adapter\Annotation\Features; use Yandex\Allure\Adapter\Annotation\Stories; diff --git a/dev/tests/verification/Resources/ActionGroupWithMultipleParameterSelectorsFromDefaultArgument.txt b/dev/tests/verification/Resources/ActionGroupWithMultipleParameterSelectorsFromDefaultArgument.txt index 3f907b9ee..620bbf5f5 100644 --- a/dev/tests/verification/Resources/ActionGroupWithMultipleParameterSelectorsFromDefaultArgument.txt +++ b/dev/tests/verification/Resources/ActionGroupWithMultipleParameterSelectorsFromDefaultArgument.txt @@ -5,6 +5,7 @@ use Magento\FunctionalTestingFramework\AcceptanceTester; use Magento\FunctionalTestingFramework\DataGenerator\Handlers\DataObjectHandler; use Magento\FunctionalTestingFramework\DataGenerator\Persist\DataPersistenceHandler; use Magento\FunctionalTestingFramework\DataGenerator\Objects\EntityDataObject; +use Magento\FunctionalTestingFramework\DataGenerator\Handlers\CredentialStore; use \Codeception\Util\Locator; use Yandex\Allure\Adapter\Annotation\Features; use Yandex\Allure\Adapter\Annotation\Stories; diff --git a/dev/tests/verification/Resources/ActionGroupWithNoArguments.txt b/dev/tests/verification/Resources/ActionGroupWithNoArguments.txt index b2fa98e5c..b0f709ac9 100644 --- a/dev/tests/verification/Resources/ActionGroupWithNoArguments.txt +++ b/dev/tests/verification/Resources/ActionGroupWithNoArguments.txt @@ -5,6 +5,7 @@ use Magento\FunctionalTestingFramework\AcceptanceTester; use Magento\FunctionalTestingFramework\DataGenerator\Handlers\DataObjectHandler; use Magento\FunctionalTestingFramework\DataGenerator\Persist\DataPersistenceHandler; use Magento\FunctionalTestingFramework\DataGenerator\Objects\EntityDataObject; +use Magento\FunctionalTestingFramework\DataGenerator\Handlers\CredentialStore; use \Codeception\Util\Locator; use Yandex\Allure\Adapter\Annotation\Features; use Yandex\Allure\Adapter\Annotation\Stories; diff --git a/dev/tests/verification/Resources/ActionGroupWithNoDefaultTest.txt b/dev/tests/verification/Resources/ActionGroupWithNoDefaultTest.txt index 8739d8fa0..a5119c368 100644 --- a/dev/tests/verification/Resources/ActionGroupWithNoDefaultTest.txt +++ b/dev/tests/verification/Resources/ActionGroupWithNoDefaultTest.txt @@ -5,6 +5,7 @@ use Magento\FunctionalTestingFramework\AcceptanceTester; use Magento\FunctionalTestingFramework\DataGenerator\Handlers\DataObjectHandler; use Magento\FunctionalTestingFramework\DataGenerator\Persist\DataPersistenceHandler; use Magento\FunctionalTestingFramework\DataGenerator\Objects\EntityDataObject; +use Magento\FunctionalTestingFramework\DataGenerator\Handlers\CredentialStore; use \Codeception\Util\Locator; use Yandex\Allure\Adapter\Annotation\Features; use Yandex\Allure\Adapter\Annotation\Stories; diff --git a/dev/tests/verification/Resources/ActionGroupWithPassedArgumentAndStringSelectorParam.txt b/dev/tests/verification/Resources/ActionGroupWithPassedArgumentAndStringSelectorParam.txt index c7c305cb8..33d3d3d13 100644 --- a/dev/tests/verification/Resources/ActionGroupWithPassedArgumentAndStringSelectorParam.txt +++ b/dev/tests/verification/Resources/ActionGroupWithPassedArgumentAndStringSelectorParam.txt @@ -5,6 +5,7 @@ use Magento\FunctionalTestingFramework\AcceptanceTester; use Magento\FunctionalTestingFramework\DataGenerator\Handlers\DataObjectHandler; use Magento\FunctionalTestingFramework\DataGenerator\Persist\DataPersistenceHandler; use Magento\FunctionalTestingFramework\DataGenerator\Objects\EntityDataObject; +use Magento\FunctionalTestingFramework\DataGenerator\Handlers\CredentialStore; use \Codeception\Util\Locator; use Yandex\Allure\Adapter\Annotation\Features; use Yandex\Allure\Adapter\Annotation\Stories; diff --git a/dev/tests/verification/Resources/ActionGroupWithPersistedData.txt b/dev/tests/verification/Resources/ActionGroupWithPersistedData.txt index 63fd6af52..133553573 100644 --- a/dev/tests/verification/Resources/ActionGroupWithPersistedData.txt +++ b/dev/tests/verification/Resources/ActionGroupWithPersistedData.txt @@ -5,6 +5,7 @@ use Magento\FunctionalTestingFramework\AcceptanceTester; use Magento\FunctionalTestingFramework\DataGenerator\Handlers\DataObjectHandler; use Magento\FunctionalTestingFramework\DataGenerator\Persist\DataPersistenceHandler; use Magento\FunctionalTestingFramework\DataGenerator\Objects\EntityDataObject; +use Magento\FunctionalTestingFramework\DataGenerator\Handlers\CredentialStore; use \Codeception\Util\Locator; use Yandex\Allure\Adapter\Annotation\Features; use Yandex\Allure\Adapter\Annotation\Stories; diff --git a/dev/tests/verification/Resources/ActionGroupWithSimpleDataUsageFromDefaultArgument.txt b/dev/tests/verification/Resources/ActionGroupWithSimpleDataUsageFromDefaultArgument.txt index f27695b2b..b5c871a0d 100644 --- a/dev/tests/verification/Resources/ActionGroupWithSimpleDataUsageFromDefaultArgument.txt +++ b/dev/tests/verification/Resources/ActionGroupWithSimpleDataUsageFromDefaultArgument.txt @@ -5,6 +5,7 @@ use Magento\FunctionalTestingFramework\AcceptanceTester; use Magento\FunctionalTestingFramework\DataGenerator\Handlers\DataObjectHandler; use Magento\FunctionalTestingFramework\DataGenerator\Persist\DataPersistenceHandler; use Magento\FunctionalTestingFramework\DataGenerator\Objects\EntityDataObject; +use Magento\FunctionalTestingFramework\DataGenerator\Handlers\CredentialStore; use \Codeception\Util\Locator; use Yandex\Allure\Adapter\Annotation\Features; use Yandex\Allure\Adapter\Annotation\Stories; diff --git a/dev/tests/verification/Resources/ActionGroupWithSimpleDataUsageFromPassedArgument.txt b/dev/tests/verification/Resources/ActionGroupWithSimpleDataUsageFromPassedArgument.txt index ac56ca3f3..d3b910c0a 100644 --- a/dev/tests/verification/Resources/ActionGroupWithSimpleDataUsageFromPassedArgument.txt +++ b/dev/tests/verification/Resources/ActionGroupWithSimpleDataUsageFromPassedArgument.txt @@ -5,6 +5,7 @@ use Magento\FunctionalTestingFramework\AcceptanceTester; use Magento\FunctionalTestingFramework\DataGenerator\Handlers\DataObjectHandler; use Magento\FunctionalTestingFramework\DataGenerator\Persist\DataPersistenceHandler; use Magento\FunctionalTestingFramework\DataGenerator\Objects\EntityDataObject; +use Magento\FunctionalTestingFramework\DataGenerator\Handlers\CredentialStore; use \Codeception\Util\Locator; use Yandex\Allure\Adapter\Annotation\Features; use Yandex\Allure\Adapter\Annotation\Stories; diff --git a/dev/tests/verification/Resources/ActionGroupWithSingleParameterSelectorFromDefaultArgument.txt b/dev/tests/verification/Resources/ActionGroupWithSingleParameterSelectorFromDefaultArgument.txt index 7b74e0c1d..027370779 100644 --- a/dev/tests/verification/Resources/ActionGroupWithSingleParameterSelectorFromDefaultArgument.txt +++ b/dev/tests/verification/Resources/ActionGroupWithSingleParameterSelectorFromDefaultArgument.txt @@ -5,6 +5,7 @@ use Magento\FunctionalTestingFramework\AcceptanceTester; use Magento\FunctionalTestingFramework\DataGenerator\Handlers\DataObjectHandler; use Magento\FunctionalTestingFramework\DataGenerator\Persist\DataPersistenceHandler; use Magento\FunctionalTestingFramework\DataGenerator\Objects\EntityDataObject; +use Magento\FunctionalTestingFramework\DataGenerator\Handlers\CredentialStore; use \Codeception\Util\Locator; use Yandex\Allure\Adapter\Annotation\Features; use Yandex\Allure\Adapter\Annotation\Stories; diff --git a/dev/tests/verification/Resources/ActionGroupWithSingleParameterSelectorFromPassedArgument.txt b/dev/tests/verification/Resources/ActionGroupWithSingleParameterSelectorFromPassedArgument.txt index bd71ebdf7..7900385db 100644 --- a/dev/tests/verification/Resources/ActionGroupWithSingleParameterSelectorFromPassedArgument.txt +++ b/dev/tests/verification/Resources/ActionGroupWithSingleParameterSelectorFromPassedArgument.txt @@ -5,6 +5,7 @@ use Magento\FunctionalTestingFramework\AcceptanceTester; use Magento\FunctionalTestingFramework\DataGenerator\Handlers\DataObjectHandler; use Magento\FunctionalTestingFramework\DataGenerator\Persist\DataPersistenceHandler; use Magento\FunctionalTestingFramework\DataGenerator\Objects\EntityDataObject; +use Magento\FunctionalTestingFramework\DataGenerator\Handlers\CredentialStore; use \Codeception\Util\Locator; use Yandex\Allure\Adapter\Annotation\Features; use Yandex\Allure\Adapter\Annotation\Stories; diff --git a/dev/tests/verification/Resources/ActionGroupWithStepKeyReferences.txt b/dev/tests/verification/Resources/ActionGroupWithStepKeyReferences.txt index 7b1a4d30d..28bb00d4d 100644 --- a/dev/tests/verification/Resources/ActionGroupWithStepKeyReferences.txt +++ b/dev/tests/verification/Resources/ActionGroupWithStepKeyReferences.txt @@ -5,6 +5,7 @@ use Magento\FunctionalTestingFramework\AcceptanceTester; use Magento\FunctionalTestingFramework\DataGenerator\Handlers\DataObjectHandler; use Magento\FunctionalTestingFramework\DataGenerator\Persist\DataPersistenceHandler; use Magento\FunctionalTestingFramework\DataGenerator\Objects\EntityDataObject; +use Magento\FunctionalTestingFramework\DataGenerator\Handlers\CredentialStore; use \Codeception\Util\Locator; use Yandex\Allure\Adapter\Annotation\Features; use Yandex\Allure\Adapter\Annotation\Stories; diff --git a/dev/tests/verification/Resources/ActionGroupWithTopLevelPersistedData.txt b/dev/tests/verification/Resources/ActionGroupWithTopLevelPersistedData.txt index 1881a392a..4ec6520d9 100644 --- a/dev/tests/verification/Resources/ActionGroupWithTopLevelPersistedData.txt +++ b/dev/tests/verification/Resources/ActionGroupWithTopLevelPersistedData.txt @@ -5,6 +5,7 @@ use Magento\FunctionalTestingFramework\AcceptanceTester; use Magento\FunctionalTestingFramework\DataGenerator\Handlers\DataObjectHandler; use Magento\FunctionalTestingFramework\DataGenerator\Persist\DataPersistenceHandler; use Magento\FunctionalTestingFramework\DataGenerator\Objects\EntityDataObject; +use Magento\FunctionalTestingFramework\DataGenerator\Handlers\CredentialStore; use \Codeception\Util\Locator; use Yandex\Allure\Adapter\Annotation\Features; use Yandex\Allure\Adapter\Annotation\Stories; diff --git a/dev/tests/verification/Resources/ArgumentWithSameNameAsElement.txt b/dev/tests/verification/Resources/ArgumentWithSameNameAsElement.txt index 90b195688..a97fd116e 100644 --- a/dev/tests/verification/Resources/ArgumentWithSameNameAsElement.txt +++ b/dev/tests/verification/Resources/ArgumentWithSameNameAsElement.txt @@ -5,6 +5,7 @@ use Magento\FunctionalTestingFramework\AcceptanceTester; use Magento\FunctionalTestingFramework\DataGenerator\Handlers\DataObjectHandler; use Magento\FunctionalTestingFramework\DataGenerator\Persist\DataPersistenceHandler; use Magento\FunctionalTestingFramework\DataGenerator\Objects\EntityDataObject; +use Magento\FunctionalTestingFramework\DataGenerator\Handlers\CredentialStore; use \Codeception\Util\Locator; use Yandex\Allure\Adapter\Annotation\Features; use Yandex\Allure\Adapter\Annotation\Stories; diff --git a/dev/tests/verification/Resources/AssertTest.txt b/dev/tests/verification/Resources/AssertTest.txt index 111b236ed..beb78eed1 100644 --- a/dev/tests/verification/Resources/AssertTest.txt +++ b/dev/tests/verification/Resources/AssertTest.txt @@ -5,6 +5,7 @@ use Magento\FunctionalTestingFramework\AcceptanceTester; use Magento\FunctionalTestingFramework\DataGenerator\Handlers\DataObjectHandler; use Magento\FunctionalTestingFramework\DataGenerator\Persist\DataPersistenceHandler; use Magento\FunctionalTestingFramework\DataGenerator\Objects\EntityDataObject; +use Magento\FunctionalTestingFramework\DataGenerator\Handlers\CredentialStore; use \Codeception\Util\Locator; use Yandex\Allure\Adapter\Annotation\Features; use Yandex\Allure\Adapter\Annotation\Stories; diff --git a/dev/tests/verification/Resources/BasicActionGroupTest.txt b/dev/tests/verification/Resources/BasicActionGroupTest.txt index 259016681..b8b491ef4 100644 --- a/dev/tests/verification/Resources/BasicActionGroupTest.txt +++ b/dev/tests/verification/Resources/BasicActionGroupTest.txt @@ -5,6 +5,7 @@ use Magento\FunctionalTestingFramework\AcceptanceTester; use Magento\FunctionalTestingFramework\DataGenerator\Handlers\DataObjectHandler; use Magento\FunctionalTestingFramework\DataGenerator\Persist\DataPersistenceHandler; use Magento\FunctionalTestingFramework\DataGenerator\Objects\EntityDataObject; +use Magento\FunctionalTestingFramework\DataGenerator\Handlers\CredentialStore; use \Codeception\Util\Locator; use Yandex\Allure\Adapter\Annotation\Features; use Yandex\Allure\Adapter\Annotation\Stories; diff --git a/dev/tests/verification/Resources/BasicFunctionalTest.txt b/dev/tests/verification/Resources/BasicFunctionalTest.txt index b826ecef8..4502be407 100644 --- a/dev/tests/verification/Resources/BasicFunctionalTest.txt +++ b/dev/tests/verification/Resources/BasicFunctionalTest.txt @@ -5,6 +5,7 @@ use Magento\FunctionalTestingFramework\AcceptanceTester; use Magento\FunctionalTestingFramework\DataGenerator\Handlers\DataObjectHandler; use Magento\FunctionalTestingFramework\DataGenerator\Persist\DataPersistenceHandler; use Magento\FunctionalTestingFramework\DataGenerator\Objects\EntityDataObject; +use Magento\FunctionalTestingFramework\DataGenerator\Handlers\CredentialStore; use \Codeception\Util\Locator; use Yandex\Allure\Adapter\Annotation\Features; use Yandex\Allure\Adapter\Annotation\Stories; diff --git a/dev/tests/verification/Resources/BasicMergeTest.txt b/dev/tests/verification/Resources/BasicMergeTest.txt index ad5a0c189..280630b6f 100644 --- a/dev/tests/verification/Resources/BasicMergeTest.txt +++ b/dev/tests/verification/Resources/BasicMergeTest.txt @@ -5,6 +5,7 @@ use Magento\FunctionalTestingFramework\AcceptanceTester; use Magento\FunctionalTestingFramework\DataGenerator\Handlers\DataObjectHandler; use Magento\FunctionalTestingFramework\DataGenerator\Persist\DataPersistenceHandler; use Magento\FunctionalTestingFramework\DataGenerator\Objects\EntityDataObject; +use Magento\FunctionalTestingFramework\DataGenerator\Handlers\CredentialStore; use \Codeception\Util\Locator; use Yandex\Allure\Adapter\Annotation\Features; use Yandex\Allure\Adapter\Annotation\Stories; diff --git a/dev/tests/verification/Resources/CharacterReplacementTest.txt b/dev/tests/verification/Resources/CharacterReplacementTest.txt index 466997a6f..844b08974 100644 --- a/dev/tests/verification/Resources/CharacterReplacementTest.txt +++ b/dev/tests/verification/Resources/CharacterReplacementTest.txt @@ -5,6 +5,7 @@ use Magento\FunctionalTestingFramework\AcceptanceTester; use Magento\FunctionalTestingFramework\DataGenerator\Handlers\DataObjectHandler; use Magento\FunctionalTestingFramework\DataGenerator\Persist\DataPersistenceHandler; use Magento\FunctionalTestingFramework\DataGenerator\Objects\EntityDataObject; +use Magento\FunctionalTestingFramework\DataGenerator\Handlers\CredentialStore; use \Codeception\Util\Locator; use Yandex\Allure\Adapter\Annotation\Features; use Yandex\Allure\Adapter\Annotation\Stories; diff --git a/dev/tests/verification/Resources/ChildExtendedTestAddHooks.txt b/dev/tests/verification/Resources/ChildExtendedTestAddHooks.txt index bf3d3a8c2..d8417f5cb 100644 --- a/dev/tests/verification/Resources/ChildExtendedTestAddHooks.txt +++ b/dev/tests/verification/Resources/ChildExtendedTestAddHooks.txt @@ -5,6 +5,7 @@ use Magento\FunctionalTestingFramework\AcceptanceTester; use Magento\FunctionalTestingFramework\DataGenerator\Handlers\DataObjectHandler; use Magento\FunctionalTestingFramework\DataGenerator\Persist\DataPersistenceHandler; use Magento\FunctionalTestingFramework\DataGenerator\Objects\EntityDataObject; +use Magento\FunctionalTestingFramework\DataGenerator\Handlers\CredentialStore; use \Codeception\Util\Locator; use Yandex\Allure\Adapter\Annotation\Features; use Yandex\Allure\Adapter\Annotation\Stories; diff --git a/dev/tests/verification/Resources/ChildExtendedTestMerging.txt b/dev/tests/verification/Resources/ChildExtendedTestMerging.txt index 1efe59cb5..caf382718 100644 --- a/dev/tests/verification/Resources/ChildExtendedTestMerging.txt +++ b/dev/tests/verification/Resources/ChildExtendedTestMerging.txt @@ -5,6 +5,7 @@ use Magento\FunctionalTestingFramework\AcceptanceTester; use Magento\FunctionalTestingFramework\DataGenerator\Handlers\DataObjectHandler; use Magento\FunctionalTestingFramework\DataGenerator\Persist\DataPersistenceHandler; use Magento\FunctionalTestingFramework\DataGenerator\Objects\EntityDataObject; +use Magento\FunctionalTestingFramework\DataGenerator\Handlers\CredentialStore; use \Codeception\Util\Locator; use Yandex\Allure\Adapter\Annotation\Features; use Yandex\Allure\Adapter\Annotation\Stories; diff --git a/dev/tests/verification/Resources/ChildExtendedTestNoParent.txt b/dev/tests/verification/Resources/ChildExtendedTestNoParent.txt index 3bc7b8a0a..130bc1599 100644 --- a/dev/tests/verification/Resources/ChildExtendedTestNoParent.txt +++ b/dev/tests/verification/Resources/ChildExtendedTestNoParent.txt @@ -5,6 +5,7 @@ use Magento\FunctionalTestingFramework\AcceptanceTester; use Magento\FunctionalTestingFramework\DataGenerator\Handlers\DataObjectHandler; use Magento\FunctionalTestingFramework\DataGenerator\Persist\DataPersistenceHandler; use Magento\FunctionalTestingFramework\DataGenerator\Objects\EntityDataObject; +use Magento\FunctionalTestingFramework\DataGenerator\Handlers\CredentialStore; use \Codeception\Util\Locator; use Yandex\Allure\Adapter\Annotation\Features; use Yandex\Allure\Adapter\Annotation\Stories; diff --git a/dev/tests/verification/Resources/ChildExtendedTestRemoveAction.txt b/dev/tests/verification/Resources/ChildExtendedTestRemoveAction.txt index f24c3e6d1..4fefcd657 100644 --- a/dev/tests/verification/Resources/ChildExtendedTestRemoveAction.txt +++ b/dev/tests/verification/Resources/ChildExtendedTestRemoveAction.txt @@ -5,6 +5,7 @@ use Magento\FunctionalTestingFramework\AcceptanceTester; use Magento\FunctionalTestingFramework\DataGenerator\Handlers\DataObjectHandler; use Magento\FunctionalTestingFramework\DataGenerator\Persist\DataPersistenceHandler; use Magento\FunctionalTestingFramework\DataGenerator\Objects\EntityDataObject; +use Magento\FunctionalTestingFramework\DataGenerator\Handlers\CredentialStore; use \Codeception\Util\Locator; use Yandex\Allure\Adapter\Annotation\Features; use Yandex\Allure\Adapter\Annotation\Stories; diff --git a/dev/tests/verification/Resources/ChildExtendedTestRemoveHookAction.txt b/dev/tests/verification/Resources/ChildExtendedTestRemoveHookAction.txt index 9d207e13f..5f135c6d7 100644 --- a/dev/tests/verification/Resources/ChildExtendedTestRemoveHookAction.txt +++ b/dev/tests/verification/Resources/ChildExtendedTestRemoveHookAction.txt @@ -5,6 +5,7 @@ use Magento\FunctionalTestingFramework\AcceptanceTester; use Magento\FunctionalTestingFramework\DataGenerator\Handlers\DataObjectHandler; use Magento\FunctionalTestingFramework\DataGenerator\Persist\DataPersistenceHandler; use Magento\FunctionalTestingFramework\DataGenerator\Objects\EntityDataObject; +use Magento\FunctionalTestingFramework\DataGenerator\Handlers\CredentialStore; use \Codeception\Util\Locator; use Yandex\Allure\Adapter\Annotation\Features; use Yandex\Allure\Adapter\Annotation\Stories; diff --git a/dev/tests/verification/Resources/ChildExtendedTestReplace.txt b/dev/tests/verification/Resources/ChildExtendedTestReplace.txt index 6f3c3c9b3..650544131 100644 --- a/dev/tests/verification/Resources/ChildExtendedTestReplace.txt +++ b/dev/tests/verification/Resources/ChildExtendedTestReplace.txt @@ -5,6 +5,7 @@ use Magento\FunctionalTestingFramework\AcceptanceTester; use Magento\FunctionalTestingFramework\DataGenerator\Handlers\DataObjectHandler; use Magento\FunctionalTestingFramework\DataGenerator\Persist\DataPersistenceHandler; use Magento\FunctionalTestingFramework\DataGenerator\Objects\EntityDataObject; +use Magento\FunctionalTestingFramework\DataGenerator\Handlers\CredentialStore; use \Codeception\Util\Locator; use Yandex\Allure\Adapter\Annotation\Features; use Yandex\Allure\Adapter\Annotation\Stories; diff --git a/dev/tests/verification/Resources/ChildExtendedTestReplaceHook.txt b/dev/tests/verification/Resources/ChildExtendedTestReplaceHook.txt index 2a5937381..9c05eb88e 100644 --- a/dev/tests/verification/Resources/ChildExtendedTestReplaceHook.txt +++ b/dev/tests/verification/Resources/ChildExtendedTestReplaceHook.txt @@ -5,6 +5,7 @@ use Magento\FunctionalTestingFramework\AcceptanceTester; use Magento\FunctionalTestingFramework\DataGenerator\Handlers\DataObjectHandler; use Magento\FunctionalTestingFramework\DataGenerator\Persist\DataPersistenceHandler; use Magento\FunctionalTestingFramework\DataGenerator\Objects\EntityDataObject; +use Magento\FunctionalTestingFramework\DataGenerator\Handlers\CredentialStore; use \Codeception\Util\Locator; use Yandex\Allure\Adapter\Annotation\Features; use Yandex\Allure\Adapter\Annotation\Stories; diff --git a/dev/tests/verification/Resources/DataReplacementTest.txt b/dev/tests/verification/Resources/DataReplacementTest.txt index 57f94886d..2da8b00eb 100644 --- a/dev/tests/verification/Resources/DataReplacementTest.txt +++ b/dev/tests/verification/Resources/DataReplacementTest.txt @@ -5,6 +5,7 @@ use Magento\FunctionalTestingFramework\AcceptanceTester; use Magento\FunctionalTestingFramework\DataGenerator\Handlers\DataObjectHandler; use Magento\FunctionalTestingFramework\DataGenerator\Persist\DataPersistenceHandler; use Magento\FunctionalTestingFramework\DataGenerator\Objects\EntityDataObject; +use Magento\FunctionalTestingFramework\DataGenerator\Handlers\CredentialStore; use \Codeception\Util\Locator; use Yandex\Allure\Adapter\Annotation\Features; use Yandex\Allure\Adapter\Annotation\Stories; diff --git a/dev/tests/verification/Resources/ExtendParentDataTest.txt b/dev/tests/verification/Resources/ExtendParentDataTest.txt index 5553b2e7a..a681733f0 100644 --- a/dev/tests/verification/Resources/ExtendParentDataTest.txt +++ b/dev/tests/verification/Resources/ExtendParentDataTest.txt @@ -5,6 +5,7 @@ use Magento\FunctionalTestingFramework\AcceptanceTester; use Magento\FunctionalTestingFramework\DataGenerator\Handlers\DataObjectHandler; use Magento\FunctionalTestingFramework\DataGenerator\Persist\DataPersistenceHandler; use Magento\FunctionalTestingFramework\DataGenerator\Objects\EntityDataObject; +use Magento\FunctionalTestingFramework\DataGenerator\Handlers\CredentialStore; use \Codeception\Util\Locator; use Yandex\Allure\Adapter\Annotation\Features; use Yandex\Allure\Adapter\Annotation\Stories; diff --git a/dev/tests/verification/Resources/ExtendedActionGroup.txt b/dev/tests/verification/Resources/ExtendedActionGroup.txt index c89ce4e76..93acc8910 100644 --- a/dev/tests/verification/Resources/ExtendedActionGroup.txt +++ b/dev/tests/verification/Resources/ExtendedActionGroup.txt @@ -5,6 +5,7 @@ use Magento\FunctionalTestingFramework\AcceptanceTester; use Magento\FunctionalTestingFramework\DataGenerator\Handlers\DataObjectHandler; use Magento\FunctionalTestingFramework\DataGenerator\Persist\DataPersistenceHandler; use Magento\FunctionalTestingFramework\DataGenerator\Objects\EntityDataObject; +use Magento\FunctionalTestingFramework\DataGenerator\Handlers\CredentialStore; use \Codeception\Util\Locator; use Yandex\Allure\Adapter\Annotation\Features; use Yandex\Allure\Adapter\Annotation\Stories; diff --git a/dev/tests/verification/Resources/ExtendedParameterArrayTest.txt b/dev/tests/verification/Resources/ExtendedParameterArrayTest.txt index 3c5e284eb..afdbdec3e 100644 --- a/dev/tests/verification/Resources/ExtendedParameterArrayTest.txt +++ b/dev/tests/verification/Resources/ExtendedParameterArrayTest.txt @@ -5,6 +5,7 @@ use Magento\FunctionalTestingFramework\AcceptanceTester; use Magento\FunctionalTestingFramework\DataGenerator\Handlers\DataObjectHandler; use Magento\FunctionalTestingFramework\DataGenerator\Persist\DataPersistenceHandler; use Magento\FunctionalTestingFramework\DataGenerator\Objects\EntityDataObject; +use Magento\FunctionalTestingFramework\DataGenerator\Handlers\CredentialStore; use \Codeception\Util\Locator; use Yandex\Allure\Adapter\Annotation\Features; use Yandex\Allure\Adapter\Annotation\Stories; diff --git a/dev/tests/verification/Resources/ExtendedRemoveActionGroup.txt b/dev/tests/verification/Resources/ExtendedRemoveActionGroup.txt index 6539bb8cc..2bfd08a91 100644 --- a/dev/tests/verification/Resources/ExtendedRemoveActionGroup.txt +++ b/dev/tests/verification/Resources/ExtendedRemoveActionGroup.txt @@ -5,6 +5,7 @@ use Magento\FunctionalTestingFramework\AcceptanceTester; use Magento\FunctionalTestingFramework\DataGenerator\Handlers\DataObjectHandler; use Magento\FunctionalTestingFramework\DataGenerator\Persist\DataPersistenceHandler; use Magento\FunctionalTestingFramework\DataGenerator\Objects\EntityDataObject; +use Magento\FunctionalTestingFramework\DataGenerator\Handlers\CredentialStore; use \Codeception\Util\Locator; use Yandex\Allure\Adapter\Annotation\Features; use Yandex\Allure\Adapter\Annotation\Stories; diff --git a/dev/tests/verification/Resources/HookActionsTest.txt b/dev/tests/verification/Resources/HookActionsTest.txt index e15918c1c..76947ce61 100644 --- a/dev/tests/verification/Resources/HookActionsTest.txt +++ b/dev/tests/verification/Resources/HookActionsTest.txt @@ -5,6 +5,7 @@ use Magento\FunctionalTestingFramework\AcceptanceTester; use Magento\FunctionalTestingFramework\DataGenerator\Handlers\DataObjectHandler; use Magento\FunctionalTestingFramework\DataGenerator\Persist\DataPersistenceHandler; use Magento\FunctionalTestingFramework\DataGenerator\Objects\EntityDataObject; +use Magento\FunctionalTestingFramework\DataGenerator\Handlers\CredentialStore; use \Codeception\Util\Locator; use Yandex\Allure\Adapter\Annotation\Features; use Yandex\Allure\Adapter\Annotation\Stories; diff --git a/dev/tests/verification/Resources/LocatorFunctionTest.txt b/dev/tests/verification/Resources/LocatorFunctionTest.txt index 62fd808c5..b9423d1b5 100644 --- a/dev/tests/verification/Resources/LocatorFunctionTest.txt +++ b/dev/tests/verification/Resources/LocatorFunctionTest.txt @@ -5,6 +5,7 @@ use Magento\FunctionalTestingFramework\AcceptanceTester; use Magento\FunctionalTestingFramework\DataGenerator\Handlers\DataObjectHandler; use Magento\FunctionalTestingFramework\DataGenerator\Persist\DataPersistenceHandler; use Magento\FunctionalTestingFramework\DataGenerator\Objects\EntityDataObject; +use Magento\FunctionalTestingFramework\DataGenerator\Handlers\CredentialStore; use \Codeception\Util\Locator; use Yandex\Allure\Adapter\Annotation\Features; use Yandex\Allure\Adapter\Annotation\Stories; diff --git a/dev/tests/verification/Resources/MergeMassViaInsertAfter.txt b/dev/tests/verification/Resources/MergeMassViaInsertAfter.txt index 9ffc3d452..ddfa5da12 100644 --- a/dev/tests/verification/Resources/MergeMassViaInsertAfter.txt +++ b/dev/tests/verification/Resources/MergeMassViaInsertAfter.txt @@ -5,6 +5,7 @@ use Magento\FunctionalTestingFramework\AcceptanceTester; use Magento\FunctionalTestingFramework\DataGenerator\Handlers\DataObjectHandler; use Magento\FunctionalTestingFramework\DataGenerator\Persist\DataPersistenceHandler; use Magento\FunctionalTestingFramework\DataGenerator\Objects\EntityDataObject; +use Magento\FunctionalTestingFramework\DataGenerator\Handlers\CredentialStore; use \Codeception\Util\Locator; use Yandex\Allure\Adapter\Annotation\Features; use Yandex\Allure\Adapter\Annotation\Stories; diff --git a/dev/tests/verification/Resources/MergeMassViaInsertBefore.txt b/dev/tests/verification/Resources/MergeMassViaInsertBefore.txt index a830c218c..82e2c4d6d 100644 --- a/dev/tests/verification/Resources/MergeMassViaInsertBefore.txt +++ b/dev/tests/verification/Resources/MergeMassViaInsertBefore.txt @@ -5,6 +5,7 @@ use Magento\FunctionalTestingFramework\AcceptanceTester; use Magento\FunctionalTestingFramework\DataGenerator\Handlers\DataObjectHandler; use Magento\FunctionalTestingFramework\DataGenerator\Persist\DataPersistenceHandler; use Magento\FunctionalTestingFramework\DataGenerator\Objects\EntityDataObject; +use Magento\FunctionalTestingFramework\DataGenerator\Handlers\CredentialStore; use \Codeception\Util\Locator; use Yandex\Allure\Adapter\Annotation\Features; use Yandex\Allure\Adapter\Annotation\Stories; diff --git a/dev/tests/verification/Resources/MergedActionGroupTest.txt b/dev/tests/verification/Resources/MergedActionGroupTest.txt index f1d73dba5..7862b0036 100644 --- a/dev/tests/verification/Resources/MergedActionGroupTest.txt +++ b/dev/tests/verification/Resources/MergedActionGroupTest.txt @@ -5,6 +5,7 @@ use Magento\FunctionalTestingFramework\AcceptanceTester; use Magento\FunctionalTestingFramework\DataGenerator\Handlers\DataObjectHandler; use Magento\FunctionalTestingFramework\DataGenerator\Persist\DataPersistenceHandler; use Magento\FunctionalTestingFramework\DataGenerator\Objects\EntityDataObject; +use Magento\FunctionalTestingFramework\DataGenerator\Handlers\CredentialStore; use \Codeception\Util\Locator; use Yandex\Allure\Adapter\Annotation\Features; use Yandex\Allure\Adapter\Annotation\Stories; diff --git a/dev/tests/verification/Resources/MergedReferencesTest.txt b/dev/tests/verification/Resources/MergedReferencesTest.txt index fe0c9498f..39111ad40 100644 --- a/dev/tests/verification/Resources/MergedReferencesTest.txt +++ b/dev/tests/verification/Resources/MergedReferencesTest.txt @@ -5,6 +5,7 @@ use Magento\FunctionalTestingFramework\AcceptanceTester; use Magento\FunctionalTestingFramework\DataGenerator\Handlers\DataObjectHandler; use Magento\FunctionalTestingFramework\DataGenerator\Persist\DataPersistenceHandler; use Magento\FunctionalTestingFramework\DataGenerator\Objects\EntityDataObject; +use Magento\FunctionalTestingFramework\DataGenerator\Handlers\CredentialStore; use \Codeception\Util\Locator; use Yandex\Allure\Adapter\Annotation\Features; use Yandex\Allure\Adapter\Annotation\Stories; diff --git a/dev/tests/verification/Resources/MultipleActionGroupsTest.txt b/dev/tests/verification/Resources/MultipleActionGroupsTest.txt index 18f6df3a9..1b5a74c94 100644 --- a/dev/tests/verification/Resources/MultipleActionGroupsTest.txt +++ b/dev/tests/verification/Resources/MultipleActionGroupsTest.txt @@ -5,6 +5,7 @@ use Magento\FunctionalTestingFramework\AcceptanceTester; use Magento\FunctionalTestingFramework\DataGenerator\Handlers\DataObjectHandler; use Magento\FunctionalTestingFramework\DataGenerator\Persist\DataPersistenceHandler; use Magento\FunctionalTestingFramework\DataGenerator\Objects\EntityDataObject; +use Magento\FunctionalTestingFramework\DataGenerator\Handlers\CredentialStore; use \Codeception\Util\Locator; use Yandex\Allure\Adapter\Annotation\Features; use Yandex\Allure\Adapter\Annotation\Stories; diff --git a/dev/tests/verification/Resources/PageReplacementTest.txt b/dev/tests/verification/Resources/PageReplacementTest.txt index f9fda51b2..1422a24ba 100644 --- a/dev/tests/verification/Resources/PageReplacementTest.txt +++ b/dev/tests/verification/Resources/PageReplacementTest.txt @@ -5,6 +5,7 @@ use Magento\FunctionalTestingFramework\AcceptanceTester; use Magento\FunctionalTestingFramework\DataGenerator\Handlers\DataObjectHandler; use Magento\FunctionalTestingFramework\DataGenerator\Persist\DataPersistenceHandler; use Magento\FunctionalTestingFramework\DataGenerator\Objects\EntityDataObject; +use Magento\FunctionalTestingFramework\DataGenerator\Handlers\CredentialStore; use \Codeception\Util\Locator; use Yandex\Allure\Adapter\Annotation\Features; use Yandex\Allure\Adapter\Annotation\Stories; diff --git a/dev/tests/verification/Resources/ParameterArrayTest.txt b/dev/tests/verification/Resources/ParameterArrayTest.txt index d56a13386..4b56eb15c 100644 --- a/dev/tests/verification/Resources/ParameterArrayTest.txt +++ b/dev/tests/verification/Resources/ParameterArrayTest.txt @@ -5,6 +5,7 @@ use Magento\FunctionalTestingFramework\AcceptanceTester; use Magento\FunctionalTestingFramework\DataGenerator\Handlers\DataObjectHandler; use Magento\FunctionalTestingFramework\DataGenerator\Persist\DataPersistenceHandler; use Magento\FunctionalTestingFramework\DataGenerator\Objects\EntityDataObject; +use Magento\FunctionalTestingFramework\DataGenerator\Handlers\CredentialStore; use \Codeception\Util\Locator; use Yandex\Allure\Adapter\Annotation\Features; use Yandex\Allure\Adapter\Annotation\Stories; diff --git a/dev/tests/verification/Resources/ParentExtendedTest.txt b/dev/tests/verification/Resources/ParentExtendedTest.txt index a79783217..a8e74e493 100644 --- a/dev/tests/verification/Resources/ParentExtendedTest.txt +++ b/dev/tests/verification/Resources/ParentExtendedTest.txt @@ -5,6 +5,7 @@ use Magento\FunctionalTestingFramework\AcceptanceTester; use Magento\FunctionalTestingFramework\DataGenerator\Handlers\DataObjectHandler; use Magento\FunctionalTestingFramework\DataGenerator\Persist\DataPersistenceHandler; use Magento\FunctionalTestingFramework\DataGenerator\Objects\EntityDataObject; +use Magento\FunctionalTestingFramework\DataGenerator\Handlers\CredentialStore; use \Codeception\Util\Locator; use Yandex\Allure\Adapter\Annotation\Features; use Yandex\Allure\Adapter\Annotation\Stories; diff --git a/dev/tests/verification/Resources/PersistedAndXmlEntityArguments.txt b/dev/tests/verification/Resources/PersistedAndXmlEntityArguments.txt index 9f60794e2..8ff0d58bd 100644 --- a/dev/tests/verification/Resources/PersistedAndXmlEntityArguments.txt +++ b/dev/tests/verification/Resources/PersistedAndXmlEntityArguments.txt @@ -5,6 +5,7 @@ use Magento\FunctionalTestingFramework\AcceptanceTester; use Magento\FunctionalTestingFramework\DataGenerator\Handlers\DataObjectHandler; use Magento\FunctionalTestingFramework\DataGenerator\Persist\DataPersistenceHandler; use Magento\FunctionalTestingFramework\DataGenerator\Objects\EntityDataObject; +use Magento\FunctionalTestingFramework\DataGenerator\Handlers\CredentialStore; use \Codeception\Util\Locator; use Yandex\Allure\Adapter\Annotation\Features; use Yandex\Allure\Adapter\Annotation\Stories; diff --git a/dev/tests/verification/Resources/PersistedReplacementTest.txt b/dev/tests/verification/Resources/PersistedReplacementTest.txt index 4e85b0085..f4f9b0298 100644 --- a/dev/tests/verification/Resources/PersistedReplacementTest.txt +++ b/dev/tests/verification/Resources/PersistedReplacementTest.txt @@ -5,6 +5,7 @@ use Magento\FunctionalTestingFramework\AcceptanceTester; use Magento\FunctionalTestingFramework\DataGenerator\Handlers\DataObjectHandler; use Magento\FunctionalTestingFramework\DataGenerator\Persist\DataPersistenceHandler; use Magento\FunctionalTestingFramework\DataGenerator\Objects\EntityDataObject; +use Magento\FunctionalTestingFramework\DataGenerator\Handlers\CredentialStore; use \Codeception\Util\Locator; use Yandex\Allure\Adapter\Annotation\Features; use Yandex\Allure\Adapter\Annotation\Stories; @@ -52,6 +53,7 @@ class PersistedReplacementTestCest $I->fillField("#selector", "StringBefore " . $createdData->getCreatedDataByName('firstname') . " StringAfter"); $I->fillField("#" . $createdData->getCreatedDataByName('firstname'), "input"); $I->fillField("#" . getenv("MAGENTO_BASE_URL") . "#" . $createdData->getCreatedDataByName('firstname'), "input"); + $I->fillField("#" . CredentialStore::getInstance()->getSecret("SECRET_PARAM") . "#" . $createdData->getCreatedDataByName('firstname'), "input"); $I->dragAndDrop("#" . $createdData->getCreatedDataByName('firstname'), $createdData->getCreatedDataByName('lastname')); $I->conditionalClick($createdData->getCreatedDataByName('lastname'), "#" . $createdData->getCreatedDataByName('firstname'), true); $I->amOnUrl($createdData->getCreatedDataByName('firstname') . ".html"); diff --git a/dev/tests/verification/Resources/PersistenceCustomFieldsTest.txt b/dev/tests/verification/Resources/PersistenceCustomFieldsTest.txt index 51da945b2..eb9f2d234 100644 --- a/dev/tests/verification/Resources/PersistenceCustomFieldsTest.txt +++ b/dev/tests/verification/Resources/PersistenceCustomFieldsTest.txt @@ -5,6 +5,7 @@ use Magento\FunctionalTestingFramework\AcceptanceTester; use Magento\FunctionalTestingFramework\DataGenerator\Handlers\DataObjectHandler; use Magento\FunctionalTestingFramework\DataGenerator\Persist\DataPersistenceHandler; use Magento\FunctionalTestingFramework\DataGenerator\Objects\EntityDataObject; +use Magento\FunctionalTestingFramework\DataGenerator\Handlers\CredentialStore; use \Codeception\Util\Locator; use Yandex\Allure\Adapter\Annotation\Features; use Yandex\Allure\Adapter\Annotation\Stories; diff --git a/dev/tests/verification/Resources/SectionReplacementTest.txt b/dev/tests/verification/Resources/SectionReplacementTest.txt index f0c71a7fa..d6afcaa5e 100644 --- a/dev/tests/verification/Resources/SectionReplacementTest.txt +++ b/dev/tests/verification/Resources/SectionReplacementTest.txt @@ -5,6 +5,7 @@ use Magento\FunctionalTestingFramework\AcceptanceTester; use Magento\FunctionalTestingFramework\DataGenerator\Handlers\DataObjectHandler; use Magento\FunctionalTestingFramework\DataGenerator\Persist\DataPersistenceHandler; use Magento\FunctionalTestingFramework\DataGenerator\Objects\EntityDataObject; +use Magento\FunctionalTestingFramework\DataGenerator\Handlers\CredentialStore; use \Codeception\Util\Locator; use Yandex\Allure\Adapter\Annotation\Features; use Yandex\Allure\Adapter\Annotation\Stories; diff --git a/dev/tests/verification/TestModule/Test/PersistedReplacementTest.xml b/dev/tests/verification/TestModule/Test/PersistedReplacementTest.xml index 26885fda4..8fc8fd19a 100644 --- a/dev/tests/verification/TestModule/Test/PersistedReplacementTest.xml +++ b/dev/tests/verification/TestModule/Test/PersistedReplacementTest.xml @@ -17,6 +17,7 @@ <fillField stepKey="inputReplace" selector="#selector" userInput="StringBefore $createdData.firstname$ StringAfter"/> <fillField stepKey="selectorReplace" selector="#$createdData.firstname$" userInput="input"/> <fillField stepKey="selectorReplace2" selector="#{{_ENV.MAGENTO_BASE_URL}}#$createdData.firstname$" userInput="input"/> + <fillField stepKey="selectorReplace3" selector="#{{_CREDS.SECRET_PARAM}}#$createdData.firstname$" userInput="input"/> <dragAndDrop stepKey="selector12Replace" selector1="#$createdData.firstname$" selector2="$createdData.lastname$"/> <conditionalClick stepKey="dependentSelectorReplace" dependentSelector="#$createdData.firstname$" selector="$createdData.lastname$" visible="true"/> <amOnUrl stepKey="urlReplace" url="$createdData.firstname$.html"/> diff --git a/etc/config/.credentials.example b/etc/config/.credentials.example new file mode 100644 index 000000000..ea8b03480 --- /dev/null +++ b/etc/config/.credentials.example @@ -0,0 +1,75 @@ +#carriers/fedex/account= +#carriers/fedex/meter_number= +#carriers/fedex/key= +#carriers/fedex/password= + +#carriers/ups/password= +#carriers/ups/username= +#carriers/ups/access_license_number= +#carriers/ups/shipper_number= + +#carriers/usps/userid= +#carriers/usps/password= + +#carriers_dhl_id_us= +#carriers_dhl_password_us= +#carriers_dhl_account_us= + +#carriers_dhl_id_eu= +#carriers_dhl_password_eu= +#carriers_dhl_account_eu= + + +#payment_authorizenet_login= +#payment_authorizenet_trans_key= +#payment_authorizenet_trans_md5= + +#authorizenet_fraud_review_login= +#authorizenet_fraud_review_trans_key= +#authorizenet_fraud_review_md5= + +#braintree_enabled_fraud_merchant_account_id= +#braintree_enabled_fraud_merchant_id= +#braintree_enabled_fraud_public_key= +#braintree_enabled_fraud_private_key= + +#braintree_disabled_fraud_merchant_account_id= +#braintree_disabled_fraud_merchant_id= +#braintree_disabled_fraud_public_key= +#braintree_disabled_fraud_private_key= + +#payment/paypal_group_all_in_one/wpp_usuk/wpp_required_settings/wpp_and_express_checkout/business_account= +#payment/paypal_group_all_in_one/wpp_usuk/wpp_required_settings/wpp_and_express_checkout/api_username= +#payment/paypal_group_all_in_one/wpp_usuk/wpp_required_settings/wpp_and_express_checkout/api_password= +#payment/paypal_group_all_in_one/wpp_usuk/wpp_required_settings/wpp_and_express_checkout/api_signature= +#payment/paypal_express/merchant_id= + +#payflow_pro_fraud_protection_enabled_business_account= +#payflow_pro_fraud_protection_enabled_partner= +#payflow_pro_fraud_protection_enabled_user= +#payflow_pro_fraud_protection_enabled_pwd= +#payflow_pro_fraud_protection_enabled_vendor= + +#payflow_pro_business_account= +#payflow_pro_partner= +#payflow_pro_user= +#payflow_pro_pwd= +#payflow_pro_vendor= + +#payflow_link_business_account_email= +#payflow_link_partner= +#payflow_link_user= +#payflow_link_password= +#payflow_link_vendor= + +#payment/paypal_group_all_in_one/payments_pro_hosted_solution_with_express_checkout/pphs_required_settings/pphs_required_settings_pphs/business_account= +#payment/paypal_group_all_in_one/payments_pro_hosted_solution_with_express_checkout/pphs_required_settings/pphs_required_settings_pphs/api_username= +#payment/paypal_group_all_in_one/payments_pro_hosted_solution_with_express_checkout/pphs_required_settings/pphs_required_settings_pphs/api_password= +#payment/paypal_group_all_in_one/payments_pro_hosted_solution_with_express_checkout/pphs_required_settings/pphs_required_settings_pphs/api_signature= + +#payment/paypal_alternative_payment_methods/express_checkout_us/express_checkout_required/express_checkout_required_express_checkout/business_account= +#payment/paypal_alternative_payment_methods/express_checkout_us/express_checkout_required/express_checkout_required_express_checkout/api_username= +#payment/paypal_alternative_payment_methods/express_checkout_us/express_checkout_required/express_checkout_required_express_checkout/api_password= +#payment/paypal_alternative_payment_methods/express_checkout_us/express_checkout_required/express_checkout_required_express_checkout/api_signature= + +#fraud_protection/signifyd/api_key= \ No newline at end of file diff --git a/src/Magento/FunctionalTestingFramework/Console/BuildProjectCommand.php b/src/Magento/FunctionalTestingFramework/Console/BuildProjectCommand.php index 66378da8d..88b15efd3 100644 --- a/src/Magento/FunctionalTestingFramework/Console/BuildProjectCommand.php +++ b/src/Magento/FunctionalTestingFramework/Console/BuildProjectCommand.php @@ -21,6 +21,7 @@ class BuildProjectCommand extends Command { const DEFAULT_YAML_INLINE_DEPTH = 10; + const CREDENTIALS_FILE_PATH = TESTS_BP . DIRECTORY_SEPARATOR . '.credentials.example'; /** * Env processor manages .env files. @@ -72,6 +73,7 @@ protected function execute(InputInterface $input, OutputInterface $output) $setupEnvCommand->run($commandInput, $output); + // TODO can we just import the codecept symfony command? $codeceptBuildCommand = realpath(PROJECT_ROOT . '/vendor/bin/codecept') . ' build'; $process = new Process($codeceptBuildCommand); @@ -126,5 +128,12 @@ private function generateConfigFiles(OutputInterface $output) $output->writeln("functional.suite.yml applied to " . TESTS_BP . DIRECTORY_SEPARATOR . 'tests' . DIRECTORY_SEPARATOR . 'functional.suite.yml'); } + + $fileSystem->copy( + FW_BP . '/etc/config/.credentials.example', + self::CREDENTIALS_FILE_PATH + ); + + $output->writeln('.credentials.example successfully applied.'); } } diff --git a/src/Magento/FunctionalTestingFramework/DataGenerator/Handlers/CredentialStore.php b/src/Magento/FunctionalTestingFramework/DataGenerator/Handlers/CredentialStore.php new file mode 100644 index 000000000..edd5a7c18 --- /dev/null +++ b/src/Magento/FunctionalTestingFramework/DataGenerator/Handlers/CredentialStore.php @@ -0,0 +1,110 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\FunctionalTestingFramework\DataGenerator\Handlers; + +use Magento\FunctionalTestingFramework\Config\MftfApplicationConfig; +use Magento\FunctionalTestingFramework\Console\BuildProjectCommand; +use Magento\FunctionalTestingFramework\Exceptions\TestFrameworkException; +use Magento\FunctionalTestingFramework\Util\Logger\LoggingUtil; + +class CredentialStore +{ + /** + * Singletone instnace + * + * @var CredentialStore + */ + private static $INSTANCE = null; + + /** + * Key/Value paris of credential names and their corresponding values + * + * @var array + */ + private $credentials = []; + + /** + * Static singleton getter for CredentialStore Instance + * + * @return CredentialStore + */ + public static function getInstance() + { + if (self::$INSTANCE == null) { + self::$INSTANCE = new CredentialStore(); + } + + return self::$INSTANCE; + } + + /** + * CredentialStore constructor. + */ + private function __construct() + { + $this->readInCredentialsFile(); + } + + /** + * Returns the value of a secret based on corresponding key + * + * @param string $key + * @return string|null + * @throws TestFrameworkException + */ + public function getSecret($key) + { + if (!array_key_exists($key, $this->credentials)) { + throw new TestFrameworkException( + "{$key} not defined in .credentials, please provide a value in order to use this secret in a test." + ); + } + + // log here for verbose config + if (MftfApplicationConfig::getConfig()->verboseEnabled()) { + LoggingUtil::getInstance()->getLogger(CredentialStore::class)->debug( + "retrieving secret for key name {$key}" + ); + } + + return $this->credentials[$key] ?? null; + } + + /** + * Private function which reads in secret key/values from .credentials file and stores in memory as key/value pair. + * + * @return void + * @throws TestFrameworkException + */ + private function readInCredentialsFile() + { + $credsFilePath = str_replace( + '.credentials.example', + '.credentials', + BuildProjectCommand::CREDENTIALS_FILE_PATH + ); + + if (!file_exists($credsFilePath)) { + throw new TestFrameworkException( + "Cannot find .credentials file, please create in " + . TESTS_BP . " in order to reference sensitive information" + ); + } + + $credContents = file($credsFilePath, FILE_IGNORE_NEW_LINES); + foreach ($credContents as $credValue) { + if (substr($credValue, 0, 1) === '#' || empty($credValue)) { + continue; + } + + list($key, $value) = explode("=", $credValue); + if (!empty($value)) { + $this->credentials[$key] = $value; + } + } + } +} diff --git a/src/Magento/FunctionalTestingFramework/Test/Objects/ActionObject.php b/src/Magento/FunctionalTestingFramework/Test/Objects/ActionObject.php index 1fcff34db..6b361daa0 100644 --- a/src/Magento/FunctionalTestingFramework/Test/Objects/ActionObject.php +++ b/src/Magento/FunctionalTestingFramework/Test/Objects/ActionObject.php @@ -23,6 +23,12 @@ class ActionObject { const __ENV = "_ENV"; + const __CREDS = "_CREDS"; + const RUNTIME_REFERENCES = [ + self::__ENV, + self::__CREDS + ]; + const DATA_ENABLED_ATTRIBUTES = [ "userInput", "parameterArray", @@ -499,8 +505,8 @@ private function findAndReplaceReferences($objectHandler, $inputString) $obj = $objectHandler->getObject($objName); - // Leave {{_ENV.VARIABLE}} references to be replaced in TestGenerator with getenv("VARIABLE") - if ($objName === ActionObject::__ENV) { + // Leave runtime references to be replaced in TestGenerator with getter function accessing "VARIABLE" + if (in_array($objName, ActionObject::RUNTIME_REFERENCES)) { continue; } diff --git a/src/Magento/FunctionalTestingFramework/Util/Env/EnvProcessor.php b/src/Magento/FunctionalTestingFramework/Util/Env/EnvProcessor.php index 248e11288..a954e0681 100644 --- a/src/Magento/FunctionalTestingFramework/Util/Env/EnvProcessor.php +++ b/src/Magento/FunctionalTestingFramework/Util/Env/EnvProcessor.php @@ -106,11 +106,7 @@ public function putEnvFile(array $config = []) $envData .= $key . '=' . $value . PHP_EOL; } - if ($this->envExists) { - file_put_contents($this->envFile, $envData, FILE_APPEND); - } else { - file_put_contents($this->envFile, $envData); - } + file_put_contents($this->envFile, $envData); } /** diff --git a/src/Magento/FunctionalTestingFramework/Util/TestGenerator.php b/src/Magento/FunctionalTestingFramework/Util/TestGenerator.php index 00203a7e7..022ccefc4 100644 --- a/src/Magento/FunctionalTestingFramework/Util/TestGenerator.php +++ b/src/Magento/FunctionalTestingFramework/Util/TestGenerator.php @@ -6,6 +6,7 @@ namespace Magento\FunctionalTestingFramework\Util; +use Magento\FunctionalTestingFramework\DataGenerator\Handlers\CredentialStore; use Magento\FunctionalTestingFramework\DataGenerator\Objects\EntityDataObject; use Magento\FunctionalTestingFramework\Exceptions\TestReferenceException; use Magento\FunctionalTestingFramework\Suite\Handlers\SuiteObjectHandler; @@ -285,6 +286,7 @@ private function generateUseStatementsPhp() $useStatementsPhp .= "use Magento\FunctionalTestingFramework\DataGenerator\Handlers\DataObjectHandler;\n"; $useStatementsPhp .= "use Magento\FunctionalTestingFramework\DataGenerator\Persist\DataPersistenceHandler;\n"; $useStatementsPhp .= "use Magento\FunctionalTestingFramework\DataGenerator\Objects\EntityDataObject;\n"; + $useStatementsPhp .= "use Magento\FunctionalTestingFramework\DataGenerator\Handlers\CredentialStore;\n"; $useStatementsPhp .= "use \Codeception\Util\Locator;\n"; $allureStatements = [ @@ -747,7 +749,7 @@ public function generateStepsPhp($actionObjects, $hookObject = false, $actor = " $testSteps .= $contextSetter; $testSteps .= $deleteEntityFunctionCall; } else { - $url = $this->resolveEnvReferences([$url])[0]; + $url = $this->resolveAllRuntimeReferences([$url])[0]; $url = $this->resolveTestVariable([$url], null)[0]; $output = sprintf( "\t\t$%s->deleteEntityByUrl(%s);\n", @@ -1675,7 +1677,7 @@ private function wrapFunctionCall($actor, $action, ...$args) if (!is_array($args)) { $args = [$args]; } - $args = $this->resolveEnvReferences($args); + $args = $this->resolveAllRuntimeReferences($args); $args = $this->resolveTestVariable($args, $action->getActionOrigin()); $output .= implode(", ", array_filter($args, function($value) { return $value !== null; })) . ");\n"; return $output; @@ -1706,7 +1708,7 @@ private function wrapFunctionCallWithReturnValue($returnVariable, $actor, $actio if (!is_array($args)) { $args = [$args]; } - $args = $this->resolveEnvReferences($args); + $args = $this->resolveAllRuntimeReferences($args); $args = $this->resolveTestVariable($args, $action->getActionOrigin()); $output .= implode(", ", array_filter($args, function($value) { return $value !== null; })) . ");\n"; return $output; @@ -1716,21 +1718,21 @@ private function wrapFunctionCallWithReturnValue($returnVariable, $actor, $actio /** * Resolves {{_ENV.variable}} into getenv("variable") for test-runtime ENV referencing. * @param array $args + * @param string $regex + * @param string $func * @return array */ - private function resolveEnvReferences($args) + private function resolveRuntimeReference($args, $regex, $func) { - $envRegex = "/{{_ENV\.([\w]+)}}/"; - $newArgs = []; foreach ($args as $key => $arg) { - preg_match_all($envRegex, $arg, $matches); + preg_match_all($regex, $arg, $matches); if (!empty($matches[0])) { $fullMatch = $matches[0][0]; - $envVariable = $matches[1][0]; + $refVariable = $matches[1][0]; unset($matches); - $replacement = "getenv(\"{$envVariable}\")"; + $replacement = "{$func}(\"{$refVariable}\")"; $outputArg = $this->processQuoteBreaks($fullMatch, $arg, $replacement); $newArgs[$key] = $outputArg; @@ -1743,6 +1745,28 @@ private function resolveEnvReferences($args) return $newArgs; } + /** + * Takes a predefined list of potentially matching special paramts and they needed function replacement and performs + * replacements on the tests args. + * + * @param array $args + * @return array + */ + private function resolveAllRuntimeReferences($args) + { + $runtimeReferenceRegex = [ + "/{{_ENV\.([\w]+)}}/" => 'getenv', + "/{{_CREDS\.([\w]+)}}/" => 'CredentialStore::getInstance()->getSecret' + ]; + + $argResult = $args; + foreach ($runtimeReferenceRegex as $regex => $func) { + $argResult = $this->resolveRuntimeReference($argResult, $regex, $func); + } + + return $argResult; + } + /** * Validates parameter array format, making sure user has enclosed string with square brackets. * From 6281e31cdb127a5e177ae0515f13f04d6de94b3c Mon Sep 17 00:00:00 2001 From: Kevin Kozan <kkozan@magento.com> Date: Tue, 19 Jun 2018 21:54:00 +0300 Subject: [PATCH 066/111] MQE-917: Eliminate usage of XSD relative paths - New mftf command to generate URN (needed for MTF parity) - New UpgrateTestsCommand - Added call to above command in BuildProject if flag -u is set - UpgradeTestSchemaPaths script added to upgrade folder. --- bin/mftf | 31 ++--- .../Console/BuildProjectCommand.php | 13 +- .../Console/CleanProjectCommand.php | 1 - .../Console/CommandList.php | 50 ++++++++ .../Console/CommandListInterface.php | 20 +++ .../Console/GenerateDevUrnCommand.php | 118 ++++++++++++++++++ .../Console/GenerateSuiteCommand.php | 4 +- .../Console/GenerateTestsCommand.php | 10 +- .../Console/RunTestCommand.php | 4 +- .../Console/RunTestGroupCommand.php | 5 +- .../Console/UpgradeTestsCommand.php | 58 +++++++++ .../Upgrade/UpdateTestSchemaPaths.php | 71 +++++++++++ .../Upgrade/UpgradeInterface.php | 22 ++++ .../Upgrade/UpgradeScriptList.php | 42 +++++++ .../Upgrade/UpgradeScriptListInterface.php | 20 +++ 15 files changed, 444 insertions(+), 25 deletions(-) create mode 100644 src/Magento/FunctionalTestingFramework/Console/CommandList.php create mode 100644 src/Magento/FunctionalTestingFramework/Console/CommandListInterface.php create mode 100644 src/Magento/FunctionalTestingFramework/Console/GenerateDevUrnCommand.php create mode 100644 src/Magento/FunctionalTestingFramework/Console/UpgradeTestsCommand.php create mode 100644 src/Magento/FunctionalTestingFramework/Upgrade/UpdateTestSchemaPaths.php create mode 100644 src/Magento/FunctionalTestingFramework/Upgrade/UpgradeInterface.php create mode 100644 src/Magento/FunctionalTestingFramework/Upgrade/UpgradeScriptList.php create mode 100644 src/Magento/FunctionalTestingFramework/Upgrade/UpgradeScriptListInterface.php diff --git a/bin/mftf b/bin/mftf index b8c1cce80..d70ee75c3 100755 --- a/bin/mftf +++ b/bin/mftf @@ -11,27 +11,30 @@ if (PHP_SAPI !== 'cli') { exit(1); } -$autoload_path = realpath(__DIR__ . '/../../../autoload.php'); -$test_bootstrap_path = realpath(__DIR__ . '/../dev/tests/functional/_bootstrap.php'); +$autoloadPath = realpath(__DIR__ . '/../../../autoload.php'); +$testBootstrapPath = realpath(__DIR__ . '/../dev/tests/functional/_bootstrap.php'); -if (file_exists($autoload_path)) { - require_once $autoload_path; -} else { - require_once $test_bootstrap_path; +try { + if (file_exists($autoloadPath)) { + require_once $autoloadPath; + } else { + require_once $testBootstrapPath; + } +} catch (\Exception $e) { + echo 'Autoload error: ' . $e->getMessage(); + exit(1); } try { $application = new Symfony\Component\Console\Application(); $application->setName('Magento Functional Testing Framework CLI'); - $application->setVersion('1.0.0'); - $application->add(new Magento\FunctionalTestingFramework\Console\SetupEnvCommand()); - $application->add(new Magento\FunctionalTestingFramework\Console\CleanProjectCommand()); - $application->add(new Magento\FunctionalTestingFramework\Console\BuildProjectCommand()); - $application->add(new Magento\FunctionalTestingFramework\Console\GenerateSuiteCommand()); - $application->add(new Magento\FunctionalTestingFramework\Console\GenerateTestsCommand()); - $application->add(new Magento\FunctionalTestingFramework\Console\RunTestGroupCommand()); - $application->add(new Magento\FunctionalTestingFramework\Console\RunTestCommand()); + $application->setVersion('2.3.0'); + /** @var \Magento\FunctionalTestingFramework\Console\CommandListInterface $commandList */ + $commandList = new \Magento\FunctionalTestingFramework\Console\CommandList; + foreach ($commandList->getCommands() as $command) { + $application->add($command); + } $application->run(); } catch (\Exception $e) { while ($e) { diff --git a/src/Magento/FunctionalTestingFramework/Console/BuildProjectCommand.php b/src/Magento/FunctionalTestingFramework/Console/BuildProjectCommand.php index 88b15efd3..17edf557c 100644 --- a/src/Magento/FunctionalTestingFramework/Console/BuildProjectCommand.php +++ b/src/Magento/FunctionalTestingFramework/Console/BuildProjectCommand.php @@ -38,7 +38,8 @@ class BuildProjectCommand extends Command protected function configure() { $this->setName('build:project') - ->setDescription('Generate configuration files for the project. Build the Codeception project.'); + ->setDescription('Generate configuration files for the project. Build the Codeception project.') + ->addOption("upgrade", 'u', InputOption::VALUE_NONE, 'upgrade existing MFTF tests according to last major release requiements'); $this->envProcessor = new EnvProcessor(TESTS_BP . DIRECTORY_SEPARATOR . '.env'); $env = $this->envProcessor->getEnv(); foreach ($env as $key => $value) { @@ -58,6 +59,10 @@ protected function configure() */ protected function execute(InputInterface $input, OutputInterface $output) { + $resetCommand = new CleanProjectCommand(); + $resetOptions = new ArrayInput([]); + $resetCommand->run($resetOptions, $output); + $this->generateConfigFiles($output); $setupEnvCommand = new SetupEnvCommand(); @@ -87,6 +92,12 @@ function ($type, $buffer) use ($output) { } } ); + + if ($input->getOption('upgrade')) { + $upgradeCommand = new UpgradeTestsCommand(); + $upgradeOptions = new ArrayInput(['path' => TESTS_MODULE_PATH]); + $upgradeCommand->run($upgradeOptions, $output); + } } /** diff --git a/src/Magento/FunctionalTestingFramework/Console/CleanProjectCommand.php b/src/Magento/FunctionalTestingFramework/Console/CleanProjectCommand.php index c2d388898..84f85fe28 100644 --- a/src/Magento/FunctionalTestingFramework/Console/CleanProjectCommand.php +++ b/src/Magento/FunctionalTestingFramework/Console/CleanProjectCommand.php @@ -14,7 +14,6 @@ use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Filesystem\Filesystem; use Symfony\Component\Finder\Finder; -use Symfony\Component\Finder\SplFileInfo; class CleanProjectCommand extends Command { diff --git a/src/Magento/FunctionalTestingFramework/Console/CommandList.php b/src/Magento/FunctionalTestingFramework/Console/CommandList.php new file mode 100644 index 000000000..362b29296 --- /dev/null +++ b/src/Magento/FunctionalTestingFramework/Console/CommandList.php @@ -0,0 +1,50 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\FunctionalTestingFramework\Console; + +/** + * Class CommandList has a list of commands. + * @codingStandardsIgnoreFile + */ +class CommandList implements CommandListInterface +{ + /** + * List of Commands + * @var \Symfony\Component\Console\Command\Command[] + */ + private $commands; + + /** + * Constructor + * + * @param array $commands + */ + public function __construct(array $commands = []) + { + $this->commands = [ + 'build:project' => new BuildProjectCommand(), + 'reset' => new CleanProjectCommand(), + 'generate:urn-catalog' => new GenerateDevUrnCommand(), + 'generate:suite' => new GenerateSuiteCommand(), + 'generate:tests' => new GenerateTestsCommand(), + 'run:test' => new RunTestCommand(), + 'run:group' => new RunTestGroupCommand(), + 'setup:env' => new SetupEnvCommand(), + 'upgrade:tests' => new UpgradeTestsCommand(), + ] + $commands; + } + + /** + * {@inheritdoc} + */ + public function getCommands() + { + return $this->commands; + } +} diff --git a/src/Magento/FunctionalTestingFramework/Console/CommandListInterface.php b/src/Magento/FunctionalTestingFramework/Console/CommandListInterface.php new file mode 100644 index 000000000..050f7e3c0 --- /dev/null +++ b/src/Magento/FunctionalTestingFramework/Console/CommandListInterface.php @@ -0,0 +1,20 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\FunctionalTestingFramework\Console; + +/** + * Contains a list of Console commands + * @api + */ +interface CommandListInterface +{ + /** + * Gets list of command instances + * + * @return \Symfony\Component\Console\Command\Command[] + */ + public function getCommands(); +} diff --git a/src/Magento/FunctionalTestingFramework/Console/GenerateDevUrnCommand.php b/src/Magento/FunctionalTestingFramework/Console/GenerateDevUrnCommand.php new file mode 100644 index 000000000..43688e702 --- /dev/null +++ b/src/Magento/FunctionalTestingFramework/Console/GenerateDevUrnCommand.php @@ -0,0 +1,118 @@ +<?php +// @codingStandardsIgnoreFile +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types = 1); + +namespace Magento\FunctionalTestingFramework\Console; + +use Magento\FunctionalTestingFramework\Exceptions\TestFrameworkException; +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; + +class GenerateDevUrnCommand extends Command +{ + /** + * Configures the current command. + * + * @return void + */ + protected function configure() + { + $this->setName('generate:urn-catalog') + ->setDescription('This command generates an URN catalog to enable PHPStorm to recognize and highlight URNs.') + ->addArgument('path', InputArgument::REQUIRED, 'path to PHPStorm misc.xml file (typically located in [ProjectRoot]/.idea/misc.xml)'); + } + + /** + * Executes the current command. + * + * @param InputInterface $input + * @param OutputInterface $output + * @return void + * @throws \Magento\FunctionalTestingFramework\Exceptions\TestFrameworkException + */ + protected function execute(InputInterface $input, OutputInterface $output) + { + $miscXmlFilePath = $input->getArgument('path') . DIRECTORY_SEPARATOR . "misc.xml"; + $miscXmlFile = realpath($miscXmlFilePath); + + if ($miscXmlFile === false) { + $exceptionMessage = "misc.xml not found in given path '{$miscXmlFilePath}'"; + LoggingUtil::getInstance()->getLogger(GenerateDevUrnCommand::class) + ->error($exceptionMessage); + throw new TestFrameworkException($exceptionMessage); + } + $dom = new \DOMDocument('1.0'); + $dom->preserveWhiteSpace = false; + $dom->formatOutput = true; + $dom->loadXML(file_get_contents($miscXmlFile)); + + //Locate ProjectResources node, create one if none are found. + $nodeForWork = null; + foreach($dom->getElementsByTagName('component') as $child) { + if ($child->getAttribute('name') === 'ProjectResources') { + $nodeForWork = $child; + } + } + if ($nodeForWork === null) { + $project = $dom->getElementsByTagName('project')->item(0); + $nodeForWork = $dom->createElement('component'); + $nodeForWork->setAttribute('name', 'ProjectResources'); + $project->appendChild($nodeForWork); + } + + //Extract url=>location mappings that already exist, add MFTF URNs and reappend + $resources = []; + $resourceNodes = $nodeForWork->getElementsByTagName('resource'); + $resourceCount = $resourceNodes->length; + for ($i = 0; $i < $resourceCount; $i++) { + $child = $resourceNodes[0]; + $resources[$child->getAttribute('url')] = $child->getAttribute('location'); + $child->parentNode->removeChild($child); + } + + $resources = array_merge($resources, $this->generateResourcesArray()); + + foreach ($resources as $url => $location) { + $resourceNode = $dom->createElement('resource'); + $resourceNode->setAttribute('url', $url); + $resourceNode->setAttribute('location', $location); + $nodeForWork->appendChild($resourceNode); + } + + //Save output + $dom->save($miscXmlFile); + $output->writeln("MFTF URN mapping successfully added to {$miscXmlFile}."); + } + + /** + * Generates urn => location array for all MFTF schema. + * @return array + */ + private function generateResourcesArray() + { + $resourcesArray = [ + 'urn:magento:mftf:DataGenerator/etc/dataOperation.xsd' => + realpath(FW_BP . '/src/Magento/FunctionalTestingFramework/DataGenerator/etc/dataOperation.xsd'), + 'urn:magento:mftf:DataGenerator/etc/dataProfileSchema.xsd' => + realpath(FW_BP . '/src/Magento/FunctionalTestingFramework/DataGenerator/etc/dataProfileSchema.xsd'), + 'urn:magento:mftf:Page/etc/PageObject.xsd' => + realpath(FW_BP . '/src/Magento/FunctionalTestingFramework/Page/etc/PageObject.xsd'), + 'urn:magento:mftf:Page/etc/SectionObject.xsd' => + realpath(FW_BP . '/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd'), + 'urn:magento:mftf:Test/etc/actionGroupSchema.xsd' => + realpath(FW_BP . '/src/Magento/FunctionalTestingFramework/Test/etc/actionGroupSchema.xsd'), + 'urn:magento:mftf:Test/etc/testSchema.xsd' => + realpath(FW_BP . '/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd'), + 'urn:magento:mftf:Suite/etc/suiteSchema.xsd' => + realpath(FW_BP . '/src/Magento/FunctionalTestingFramework/Suite/etc/suiteSchema.xsd') + ]; + return $resourcesArray; + } + +} diff --git a/src/Magento/FunctionalTestingFramework/Console/GenerateSuiteCommand.php b/src/Magento/FunctionalTestingFramework/Console/GenerateSuiteCommand.php index d94e5fa2e..7299fef12 100644 --- a/src/Magento/FunctionalTestingFramework/Console/GenerateSuiteCommand.php +++ b/src/Magento/FunctionalTestingFramework/Console/GenerateSuiteCommand.php @@ -37,8 +37,8 @@ protected function configure() * * @param InputInterface $input * @param OutputInterface $output - * @return void - * @throws \Symfony\Component\Console\Exception\LogicException + * @return int|null|void + * @throws \Exception */ protected function execute(InputInterface $input, OutputInterface $output) { diff --git a/src/Magento/FunctionalTestingFramework/Console/GenerateTestsCommand.php b/src/Magento/FunctionalTestingFramework/Console/GenerateTestsCommand.php index bec3b0b19..80b4431da 100644 --- a/src/Magento/FunctionalTestingFramework/Console/GenerateTestsCommand.php +++ b/src/Magento/FunctionalTestingFramework/Console/GenerateTestsCommand.php @@ -46,7 +46,9 @@ protected function configure() * @param InputInterface $input * @param OutputInterface $output * @return void - * @throws \Symfony\Component\Console\Exception\LogicException|TestFrameworkException + * @throws TestFrameworkException + * @throws \Magento\FunctionalTestingFramework\Exceptions\TestReferenceException + * @throws \Magento\FunctionalTestingFramework\Exceptions\XmlException */ protected function execute(InputInterface $input, OutputInterface $output) { @@ -97,6 +99,8 @@ protected function execute(InputInterface $input, OutputInterface $output) * @param bool $debug * @param bool $verbose * @return array + * @throws \Magento\FunctionalTestingFramework\Exceptions\TestReferenceException + * @throws \Magento\FunctionalTestingFramework\Exceptions\XmlException */ private function createTestConfiguration($json, array $tests, bool $force, bool $debug, bool $verbose) { @@ -134,10 +138,10 @@ private function createTestConfiguration($json, array $tests, bool $force, bool * * @param string $json * @param array $testConfiguration - * @throws TestFrameworkException * @return array */ - private function parseTestsConfigJson($json, array $testConfiguration) { + private function parseTestsConfigJson($json, array $testConfiguration) + { if ($json === null) { return $testConfiguration; } diff --git a/src/Magento/FunctionalTestingFramework/Console/RunTestCommand.php b/src/Magento/FunctionalTestingFramework/Console/RunTestCommand.php index ed755d705..7352985a3 100644 --- a/src/Magento/FunctionalTestingFramework/Console/RunTestCommand.php +++ b/src/Magento/FunctionalTestingFramework/Console/RunTestCommand.php @@ -36,8 +36,8 @@ protected function configure() * * @param InputInterface $input * @param OutputInterface $output - * @return void - * @throws \Symfony\Component\Console\Exception\LogicException + * @return int|null|void + * @throws \Exception * * @SuppressWarnings(PHPMD.UnusedLocalVariable) */ diff --git a/src/Magento/FunctionalTestingFramework/Console/RunTestGroupCommand.php b/src/Magento/FunctionalTestingFramework/Console/RunTestGroupCommand.php index 1fda1555d..c93ccf5d3 100644 --- a/src/Magento/FunctionalTestingFramework/Console/RunTestGroupCommand.php +++ b/src/Magento/FunctionalTestingFramework/Console/RunTestGroupCommand.php @@ -42,8 +42,8 @@ protected function configure() * * @param InputInterface $input * @param OutputInterface $output - * @return void - * @throws \Symfony\Component\Console\Exception\LogicException + * @return int|null|void + * @throws \Exception * * @SuppressWarnings(PHPMD.UnusedLocalVariable) */ @@ -82,6 +82,7 @@ function ($type, $buffer) use ($output) { * * @param array $groups * @return string + * @throws \Magento\FunctionalTestingFramework\Exceptions\XmlException */ private function getGroupAndSuiteConfiguration(array $groups) { diff --git a/src/Magento/FunctionalTestingFramework/Console/UpgradeTestsCommand.php b/src/Magento/FunctionalTestingFramework/Console/UpgradeTestsCommand.php new file mode 100644 index 000000000..8e24290b5 --- /dev/null +++ b/src/Magento/FunctionalTestingFramework/Console/UpgradeTestsCommand.php @@ -0,0 +1,58 @@ +<?php +// @codingStandardsIgnoreFile +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types = 1); + +namespace Magento\FunctionalTestingFramework\Console; + +use Magento\FunctionalTestingFramework\Upgrade\UpgradeScriptList; +use Magento\FunctionalTestingFramework\Util\Logger\LoggingUtil; +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Output\OutputInterface; + +class UpgradeTestsCommand extends Command +{ + /** + * Pool of upgrade scripts to run + * + * @var \Magento\FunctionalTestingFramework\Upgrade\UpgradeScriptListInterface + */ + private $upgradeScriptsList; + + /** + * Configures the current command. + * + * @return void + */ + protected function configure() + { + $this->setName('upgrade:tests') + ->setDescription('This command will upgrade all tests in the provided path according to new MFTF Major version requirements.') + ->addArgument('path', InputArgument::REQUIRED, 'path to MFTF tests to upgrade'); + $this->upgradeScriptsList = new UpgradeScriptList(); + } + + /** + * + * + * @param InputInterface $input + * @param OutputInterface $output + * @return int|null|void + * @throws \Exception + */ + protected function execute(InputInterface $input, OutputInterface $output) + { + /** @var \Magento\FunctionalTestingFramework\Upgrade\UpgradeInterface[] $upgradeScriptObjects */ + $upgradeScriptObjects = $this->upgradeScriptsList->getUpgradeScripts(); + foreach ($upgradeScriptObjects as $upgradeScriptObject) { + $upgradeOutput = $upgradeScriptObject->execute($input); + LoggingUtil::getInstance()->getLogger(get_class($upgradeScriptObject))->info($upgradeOutput); + $output->writeln($upgradeOutput); + } + } +} diff --git a/src/Magento/FunctionalTestingFramework/Upgrade/UpdateTestSchemaPaths.php b/src/Magento/FunctionalTestingFramework/Upgrade/UpdateTestSchemaPaths.php new file mode 100644 index 000000000..80592a474 --- /dev/null +++ b/src/Magento/FunctionalTestingFramework/Upgrade/UpdateTestSchemaPaths.php @@ -0,0 +1,71 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\FunctionalTestingFramework\Upgrade; + +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Filesystem\Filesystem; +use Symfony\Component\Finder\Finder; + +/** + * Class UpdateTestSchemaPaths + * @package Magento\FunctionalTestingFramework\Upgrade + */ +class UpdateTestSchemaPaths implements UpgradeInterface +{ + /** + * Upgrades all test xml files, replacing relative schema paths to URN. + * + * @param InputInterface $input + * @return string + */ + public function execute(InputInterface $input) + { + $relativeToUrn = [ + "/src/Magento/FunctionalTestingFramework/DataGenerator/etc/dataOperation.xsd" + => "urn:magento:mftf:DataGenerator/etc/dataOperation.xsd", + "/src/Magento/FunctionalTestingFramework/DataGenerator/etc/dataProfileSchema.xsd" + => "urn:magento:mftf:DataGenerator/etc/dataProfileSchema.xsd", + "/src/Magento/FunctionalTestingFramework/Page/etc/PageObject.xsd" + => "urn:magento:mftf:Page/etc/PageObject.xsd", + "/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd" + => "urn:magento:mftf:Page/etc/SectionObject.xsd", + "/src/Magento/FunctionalTestingFramework/Test/etc/actionGroupSchema.xsd" + => "urn:magento:mftf:Test/etc/actionGroupSchema.xsd", + "/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd" + => "urn:magento:mftf:Test/etc/testSchema.xsd", + "/src/Magento/FunctionalTestingFramework/Suite/etc/suiteSchema.xsd" + => "urn:magento:mftf:Suite/etc/suiteSchema.xsd" + ]; + + $relativePatterns = []; + $urns = []; + // Prepare array of patterns to URNs for preg_replace (replace / to escapes + foreach ($relativeToUrn as $relative => $urn) { + $relativeReplaced = str_replace('/', '\/', $relative); + $relativePatterns[] = '/[.\/]+' . $relativeReplaced . '/'; + $urns[] = $urn; + } + + $testsPath = $input->getArgument('path'); + $finder = new Finder(); + $finder->files()->in($testsPath)->name("*.xml"); + + $fileSystem = new Filesystem(); + $testsUpdated = 0; + foreach ($finder->files() as $file) { + $count = 0; + $contents = $file->getContents(); + $contents = preg_replace($relativePatterns, $urns, $contents, -1, $count); + $fileSystem->dumpFile($file->getRealPath(), $contents); + if ($count > 0) { + $testsUpdated++; + } + } + + return ("Schema Path updated to use MFTF URNs in {$testsUpdated} file(s)."); + } +} diff --git a/src/Magento/FunctionalTestingFramework/Upgrade/UpgradeInterface.php b/src/Magento/FunctionalTestingFramework/Upgrade/UpgradeInterface.php new file mode 100644 index 000000000..b6905845c --- /dev/null +++ b/src/Magento/FunctionalTestingFramework/Upgrade/UpgradeInterface.php @@ -0,0 +1,22 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\FunctionalTestingFramework\Upgrade; + +use Symfony\Component\Console\Input\InputInterface; + +/** + * Upgrade script interface + */ +interface UpgradeInterface +{ + /** + * Executes upgrade script, returns output. + * @param InputInterface $input + * @return string + */ + public function execute(InputInterface $input); +} diff --git a/src/Magento/FunctionalTestingFramework/Upgrade/UpgradeScriptList.php b/src/Magento/FunctionalTestingFramework/Upgrade/UpgradeScriptList.php new file mode 100644 index 000000000..245f95d82 --- /dev/null +++ b/src/Magento/FunctionalTestingFramework/Upgrade/UpgradeScriptList.php @@ -0,0 +1,42 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\FunctionalTestingFramework\Upgrade; + +/** + * Class UpgradeScriptList has a list of scripts. + * @codingStandardsIgnoreFile + */ +class UpgradeScriptList implements UpgradeScriptListInterface +{ + /** + * Property contains all upgrade scripts. + * + * @var \Magento\FunctionalTestingFramework\Upgrade\UpgradeInterface[] + */ + private $scripts; + + /** + * Constructor + * + * @param array $scripts + */ + public function __construct(array $scripts = []) + { + $this->scripts = [ + 'upgradeTestSchema' => new UpdateTestSchemaPaths(), + ] + $scripts; + } + + /** + * {@inheritdoc} + */ + public function getUpgradeScripts() + { + return $this->scripts; + } +} diff --git a/src/Magento/FunctionalTestingFramework/Upgrade/UpgradeScriptListInterface.php b/src/Magento/FunctionalTestingFramework/Upgrade/UpgradeScriptListInterface.php new file mode 100644 index 000000000..a96316797 --- /dev/null +++ b/src/Magento/FunctionalTestingFramework/Upgrade/UpgradeScriptListInterface.php @@ -0,0 +1,20 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\FunctionalTestingFramework\Upgrade; + +/** + * Contains a list of Upgrade Scripts + * @api + */ +interface UpgradeScriptListInterface +{ + /** + * Gets list of upgrade script instances + * + * @return \Magento\FunctionalTestingFramework\Upgrade\UpgradeInterface[] + */ + public function getUpgradeScripts(); +} From 2f4f8df6a58b6fc704612b60cecd3bfbf6953b3d Mon Sep 17 00:00:00 2001 From: Alex Calandra <acalandra@magento.com> Date: Wed, 20 Jun 2018 10:40:26 -0500 Subject: [PATCH 067/111] MQE-1060: Allow magentoCLI Action To Run Without Manipulating Command That Is Sent To CLI - Adding Arguments argument for MagentoCLI --- dev/tests/verification/Resources/BasicFunctionalTest.txt | 2 +- .../verification/TestModule/Test/BasicFunctionalTest.xml | 2 +- .../FunctionalTestingFramework/Module/MagentoWebDriver.php | 7 ++++--- .../Test/etc/Actions/customActions.xsd | 7 +++++++ .../FunctionalTestingFramework/Util/TestGenerator.php | 7 ++++++- 5 files changed, 19 insertions(+), 6 deletions(-) diff --git a/dev/tests/verification/Resources/BasicFunctionalTest.txt b/dev/tests/verification/Resources/BasicFunctionalTest.txt index 4502be407..41ff55235 100644 --- a/dev/tests/verification/Resources/BasicFunctionalTest.txt +++ b/dev/tests/verification/Resources/BasicFunctionalTest.txt @@ -105,7 +105,7 @@ class BasicFunctionalTestCest $grabMultipleKey1 = $I->grabMultiple(".functionalTestSelector"); $grabTextFromKey1 = $I->grabTextFrom(".functionalTestSelector"); $grabValueFromKey1 = $I->grabValueFrom(".functionalTestSelector"); - $magentoCli1 = $I->magentoCLI("maintenance:enable"); + $magentoCli1 = $I->magentoCLI("maintenance:enable", "\"stuffHere\""); $I->comment($magentoCli1); $I->makeScreenshot("screenShotInput"); $I->maximizeWindow(); diff --git a/dev/tests/verification/TestModule/Test/BasicFunctionalTest.xml b/dev/tests/verification/TestModule/Test/BasicFunctionalTest.xml index 92b04e0ad..bc73036f3 100644 --- a/dev/tests/verification/TestModule/Test/BasicFunctionalTest.xml +++ b/dev/tests/verification/TestModule/Test/BasicFunctionalTest.xml @@ -66,7 +66,7 @@ <grabMultiple selector=".functionalTestSelector" stepKey="grabMultipleKey1" /> <grabTextFrom selector=".functionalTestSelector" stepKey="grabTextFromKey1" /> <grabValueFrom selector=".functionalTestSelector" stepKey="grabValueFromKey1" /> - <magentoCLI command="maintenance:enable" stepKey="magentoCli1"/> + <magentoCLI command="maintenance:enable" arguments=""stuffHere"" stepKey="magentoCli1"/> <makeScreenshot userInput="screenShotInput" stepKey="makeScreenshotKey1"/> <maximizeWindow stepKey="maximizeWindowKey1"/> <moveBack stepKey="moveBackKey1"/> diff --git a/src/Magento/FunctionalTestingFramework/Module/MagentoWebDriver.php b/src/Magento/FunctionalTestingFramework/Module/MagentoWebDriver.php index a1815aa70..d01224325 100644 --- a/src/Magento/FunctionalTestingFramework/Module/MagentoWebDriver.php +++ b/src/Magento/FunctionalTestingFramework/Module/MagentoWebDriver.php @@ -435,13 +435,14 @@ public function scrollToTopOfPage() /** * Takes given $command and executes it against exposed MTF CLI entry point. Returns response from server. * @param string $command - * @returns string + * @param string $arguments + * @return string */ - public function magentoCLI($command) + public function magentoCLI($command, $arguments) { $apiURL = $this->config['url'] . getenv('MAGENTO_CLI_COMMAND_PATH'); $executor = new CurlTransport(); - $executor->write($apiURL, [getenv('MAGENTO_CLI_COMMAND_PARAMETER') => $command], CurlInterface::POST, []); + $executor->write($apiURL, [getenv('MAGENTO_CLI_COMMAND_PARAMETER') => $command], CurlInterface::POST, [$arguments]); $response = $executor->read(); $executor->close(); return $response; diff --git a/src/Magento/FunctionalTestingFramework/Test/etc/Actions/customActions.xsd b/src/Magento/FunctionalTestingFramework/Test/etc/Actions/customActions.xsd index 6a27dd4ce..313e381d6 100644 --- a/src/Magento/FunctionalTestingFramework/Test/etc/Actions/customActions.xsd +++ b/src/Magento/FunctionalTestingFramework/Test/etc/Actions/customActions.xsd @@ -42,6 +42,13 @@ </xs:documentation> </xs:annotation> </xs:attribute> + <xs:attribute type="xs:string" name="arguments"> + <xs:annotation> + <xs:documentation> + Arguments for Magento CLI command, will not be escaped. + </xs:documentation> + </xs:annotation> + </xs:attribute> <xs:attributeGroup ref="commonActionAttributes"/> </xs:extension> </xs:simpleContent> diff --git a/src/Magento/FunctionalTestingFramework/Util/TestGenerator.php b/src/Magento/FunctionalTestingFramework/Util/TestGenerator.php index 022ccefc4..593579f68 100644 --- a/src/Magento/FunctionalTestingFramework/Util/TestGenerator.php +++ b/src/Magento/FunctionalTestingFramework/Util/TestGenerator.php @@ -489,6 +489,7 @@ public function generateStepsPhp($actionObjects, $hookObject = false, $actor = " $dependentSelector = null; $visible = null; $command = null; + $arguments = null; $sortOrder = null; $storeCode = null; @@ -504,6 +505,9 @@ public function generateStepsPhp($actionObjects, $hookObject = false, $actor = " if (isset($customActionAttributes['command'])) { $command = $this->addUniquenessFunctionCall($customActionAttributes['command']); } + if (isset($customActionAttributes['arguments'])) { + $arguments = $this->addUniquenessFunctionCall($customActionAttributes['arguments']); + } if (isset($customActionAttributes['attribute'])) { $attribute = $customActionAttributes['attribute']; @@ -1219,7 +1223,8 @@ public function generateStepsPhp($actionObjects, $hookObject = false, $actor = " $stepKey, $actor, $actionObject, - $command + $command, + $arguments ); $testSteps .= sprintf( "\t\t$%s->comment(\$%s);\n", From ddbe94234a8558a71874248417e7cc605316e7df Mon Sep 17 00:00:00 2001 From: Alex Calandra <acalandra@magento.com> Date: Wed, 20 Jun 2018 16:04:53 -0500 Subject: [PATCH 068/111] MQE-1060: Allow magentoCLI Action To Run Without Manipulating Command That Is Sent To CLI - Added Arguments to correct location --- .../Module/MagentoWebDriver.php | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/Magento/FunctionalTestingFramework/Module/MagentoWebDriver.php b/src/Magento/FunctionalTestingFramework/Module/MagentoWebDriver.php index d01224325..cfb559d90 100644 --- a/src/Magento/FunctionalTestingFramework/Module/MagentoWebDriver.php +++ b/src/Magento/FunctionalTestingFramework/Module/MagentoWebDriver.php @@ -438,11 +438,19 @@ public function scrollToTopOfPage() * @param string $arguments * @return string */ - public function magentoCLI($command, $arguments) + public function magentoCLI($command, $arguments = null) { $apiURL = $this->config['url'] . getenv('MAGENTO_CLI_COMMAND_PATH'); $executor = new CurlTransport(); - $executor->write($apiURL, [getenv('MAGENTO_CLI_COMMAND_PARAMETER') => $command], CurlInterface::POST, [$arguments]); + $executor->write( + $apiURL, + [ + getenv('MAGENTO_CLI_COMMAND_PARAMETER') => $command, + 'arguments' => $arguments + ], + CurlInterface::POST, + [] + ); $response = $executor->read(); $executor->close(); return $response; From 0992289ca5bd5aacaaa6b241d46a2f8f06b8c5f9 Mon Sep 17 00:00:00 2001 From: Alex Calandra <acalandra@magento.com> Date: Thu, 21 Jun 2018 11:46:49 -0500 Subject: [PATCH 069/111] MQE-1078: Enable regex attribute to resolve data references - Added regex to datareplacemtn options - Added new action to datareplacement test --- dev/tests/verification/Resources/DataReplacementTest.txt | 1 + dev/tests/verification/TestModule/Test/DataReplacementTest.xml | 1 + .../FunctionalTestingFramework/Test/Objects/ActionObject.php | 3 ++- 3 files changed, 4 insertions(+), 1 deletion(-) diff --git a/dev/tests/verification/Resources/DataReplacementTest.txt b/dev/tests/verification/Resources/DataReplacementTest.txt index 2da8b00eb..8a95eb1e8 100644 --- a/dev/tests/verification/Resources/DataReplacementTest.txt +++ b/dev/tests/verification/Resources/DataReplacementTest.txt @@ -30,6 +30,7 @@ class DataReplacementTestCest public function DataReplacementTest(AcceptanceTester $I) { $I->fillField("#selector", "StringBefore John StringAfter"); + $I->seeCurrentUrlMatches("~\/John~i"); $I->fillField("#John", "input"); $I->dragAndDrop("#John", "Doe"); $I->conditionalClick("Doe", "#John", true); diff --git a/dev/tests/verification/TestModule/Test/DataReplacementTest.xml b/dev/tests/verification/TestModule/Test/DataReplacementTest.xml index df2bb5789..ca3577373 100644 --- a/dev/tests/verification/TestModule/Test/DataReplacementTest.xml +++ b/dev/tests/verification/TestModule/Test/DataReplacementTest.xml @@ -10,6 +10,7 @@ xsi:noNamespaceSchemaLocation="../../../../../src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> <test name="DataReplacementTest"> <fillField stepKey="inputReplace" selector="#selector" userInput="StringBefore {{simpleData.firstname}} StringAfter"/> + <seeCurrentUrlMatches stepKey="seeInRegex" regex="~\/{{simpleData.firstname}}~i"/> <fillField stepKey="selectorReplace" selector="#{{simpleData.firstname}}" userInput="input"/> <dragAndDrop stepKey="selector12Replace" selector1="#{{simpleData.firstname}}" selector2="{{simpleData.lastname}}"/> <conditionalClick stepKey="dependentSelectorReplace" dependentSelector="#{{simpleData.firstname}}" selector="{{simpleData.lastname}}" visible="true"/> diff --git a/src/Magento/FunctionalTestingFramework/Test/Objects/ActionObject.php b/src/Magento/FunctionalTestingFramework/Test/Objects/ActionObject.php index 6b361daa0..b475ddfde 100644 --- a/src/Magento/FunctionalTestingFramework/Test/Objects/ActionObject.php +++ b/src/Magento/FunctionalTestingFramework/Test/Objects/ActionObject.php @@ -38,7 +38,8 @@ class ActionObject "y", "expectedResult", "actualResult", - "command" + "command", + "regex" ]; const SELECTOR_ENABLED_ATTRIBUTES = [ 'selector', From f7cb05e6c7e1259d8306bf400c147fc929328d65 Mon Sep 17 00:00:00 2001 From: Alex Calandra <acalandra@magento.com> Date: Fri, 22 Jun 2018 11:23:01 -0500 Subject: [PATCH 070/111] MQE-1068: Require Issue ID for Skipped Test - Added Schema information for Skip and issueId - Added tests around skip code - Added deprecation warning for group skip --- .../verification/Resources/SkippedTest.txt | 39 ++++++++++++++++++ .../Resources/SkippedTestTwoIssues.txt | 40 +++++++++++++++++++ .../TestModule/Test/SkippedTest.xml | 34 ++++++++++++++++ .../Tests/SkippedGenerationTest.php | 33 +++++++++++++++ etc/di.xml | 1 + .../Test/Objects/TestObject.php | 6 ++- .../Test/Util/AnnotationExtractor.php | 23 ++++++++++- .../Test/etc/mergedTestSchema.xsd | 13 ++++++ .../Util/TestGenerator.php | 6 +++ 9 files changed, 193 insertions(+), 2 deletions(-) create mode 100644 dev/tests/verification/Resources/SkippedTest.txt create mode 100644 dev/tests/verification/Resources/SkippedTestTwoIssues.txt create mode 100644 dev/tests/verification/TestModule/Test/SkippedTest.xml create mode 100644 dev/tests/verification/Tests/SkippedGenerationTest.php diff --git a/dev/tests/verification/Resources/SkippedTest.txt b/dev/tests/verification/Resources/SkippedTest.txt new file mode 100644 index 000000000..708d93ef6 --- /dev/null +++ b/dev/tests/verification/Resources/SkippedTest.txt @@ -0,0 +1,39 @@ +<?php +namespace Magento\AcceptanceTest\_default\Backend; + +use Magento\FunctionalTestingFramework\AcceptanceTester; +use Magento\FunctionalTestingFramework\DataGenerator\Handlers\DataObjectHandler; +use Magento\FunctionalTestingFramework\DataGenerator\Persist\DataPersistenceHandler; +use Magento\FunctionalTestingFramework\DataGenerator\Objects\EntityDataObject; +use Magento\FunctionalTestingFramework\DataGenerator\Handlers\CredentialStore; +use \Codeception\Util\Locator; +use Yandex\Allure\Adapter\Annotation\Features; +use Yandex\Allure\Adapter\Annotation\Stories; +use Yandex\Allure\Adapter\Annotation\Title; +use Yandex\Allure\Adapter\Annotation\Description; +use Yandex\Allure\Adapter\Annotation\Parameter; +use Yandex\Allure\Adapter\Annotation\Severity; +use Yandex\Allure\Adapter\Model\SeverityLevel; +use Yandex\Allure\Adapter\Annotation\TestCaseId; + +/** + * @Title("skippedTest") + * @Description("") + * @skipIssueId SkippedValue + */ +class SkippedTestCest +{ + /** + * @Stories({"skipped"}) + * @Severity(level = SeverityLevel::MINOR) + * @Features({"TestModule"}) + * @Parameter(name = "AcceptanceTester", value="$I") + * @param AcceptanceTester $I + * @return void + * @throws \Exception + */ + public function SkippedTest(AcceptanceTester $I, \Codeception\Scenario $scenario) + { + $scenario->skip("This test is skipped"); + } +} diff --git a/dev/tests/verification/Resources/SkippedTestTwoIssues.txt b/dev/tests/verification/Resources/SkippedTestTwoIssues.txt new file mode 100644 index 000000000..18c72c03e --- /dev/null +++ b/dev/tests/verification/Resources/SkippedTestTwoIssues.txt @@ -0,0 +1,40 @@ +<?php +namespace Magento\AcceptanceTest\_default\Backend; + +use Magento\FunctionalTestingFramework\AcceptanceTester; +use Magento\FunctionalTestingFramework\DataGenerator\Handlers\DataObjectHandler; +use Magento\FunctionalTestingFramework\DataGenerator\Persist\DataPersistenceHandler; +use Magento\FunctionalTestingFramework\DataGenerator\Objects\EntityDataObject; +use Magento\FunctionalTestingFramework\DataGenerator\Handlers\CredentialStore; +use \Codeception\Util\Locator; +use Yandex\Allure\Adapter\Annotation\Features; +use Yandex\Allure\Adapter\Annotation\Stories; +use Yandex\Allure\Adapter\Annotation\Title; +use Yandex\Allure\Adapter\Annotation\Description; +use Yandex\Allure\Adapter\Annotation\Parameter; +use Yandex\Allure\Adapter\Annotation\Severity; +use Yandex\Allure\Adapter\Model\SeverityLevel; +use Yandex\Allure\Adapter\Annotation\TestCaseId; + +/** + * @Title("skippedMultipleIssuesTest") + * @Description("") + * @skipIssueId SkippedValue + * @skipIssueId SecondSkippedValue + */ +class SkippedTestTwoIssuesCest +{ + /** + * @Stories({"skippedMultiple"}) + * @Severity(level = SeverityLevel::MINOR) + * @Features({"TestModule"}) + * @Parameter(name = "AcceptanceTester", value="$I") + * @param AcceptanceTester $I + * @return void + * @throws \Exception + */ + public function SkippedTestTwoIssues(AcceptanceTester $I, \Codeception\Scenario $scenario) + { + $scenario->skip("This test is skipped"); + } +} diff --git a/dev/tests/verification/TestModule/Test/SkippedTest.xml b/dev/tests/verification/TestModule/Test/SkippedTest.xml new file mode 100644 index 000000000..6470c967b --- /dev/null +++ b/dev/tests/verification/TestModule/Test/SkippedTest.xml @@ -0,0 +1,34 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="../../../../../src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + <test name="SkippedTest"> + <annotations> + <stories value="skipped"/> + <title value="skippedTest"/> + <description value=""/> + <severity value="AVERAGE"/> + <skip> + <issueId value="SkippedValue"/> + </skip> + </annotations> + </test> + <test name="SkippedTestTwoIssues"> + <annotations> + <stories value="skippedMultiple"/> + <title value="skippedMultipleIssuesTest"/> + <description value=""/> + <severity value="AVERAGE"/> + <skip> + <issueId value="SkippedValue"/> + <issueId value="SecondSkippedValue"/> + </skip> + </annotations> + </test> +</tests> diff --git a/dev/tests/verification/Tests/SkippedGenerationTest.php b/dev/tests/verification/Tests/SkippedGenerationTest.php new file mode 100644 index 000000000..f889b8443 --- /dev/null +++ b/dev/tests/verification/Tests/SkippedGenerationTest.php @@ -0,0 +1,33 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +namespace tests\verification\Tests; + +use tests\util\MftfTestCase; + +class SkippedGenerationTest extends MftfTestCase +{ + /** + * Tests skipped test generation. + * + * @throws \Exception + * @throws \Magento\FunctionalTestingFramework\Exceptions\TestReferenceException + */ + public function testSkippedGeneration() + { + $this->generateAndCompareTest('SkippedTest'); + } + + /** + * Tests skipped test with multiple issues generation. + * + * @throws \Exception + * @throws \Magento\FunctionalTestingFramework\Exceptions\TestReferenceException + */ + public function testMultipleSkippedIssuesGeneration() + { + $this->generateAndCompareTest('SkippedTestTwoIssues'); + } +} diff --git a/etc/di.xml b/etc/di.xml index e230bacce..fef5835e2 100644 --- a/etc/di.xml +++ b/etc/di.xml @@ -258,6 +258,7 @@ <item name="/tests/test/annotations/group" xsi:type="string">/tests/test/annotations/group</item> <item name="/tests/test/annotations/env" xsi:type="string">/tests/test/annotations/env</item> <item name="/tests/test/annotations/return" xsi:type="string">/tests/test/annotations/return</item> + <item name="/tests/test/annotations/skip/issueId" xsi:type="string">/tests/test/annotations/skip/issueId</item> </argument> </arguments> </virtualType> diff --git a/src/Magento/FunctionalTestingFramework/Test/Objects/TestObject.php b/src/Magento/FunctionalTestingFramework/Test/Objects/TestObject.php index 3569b189a..c486808f2 100644 --- a/src/Magento/FunctionalTestingFramework/Test/Objects/TestObject.php +++ b/src/Magento/FunctionalTestingFramework/Test/Objects/TestObject.php @@ -126,7 +126,11 @@ public function getParentName() */ public function isSkipped() { - if (array_key_exists('group', $this->annotations) && (in_array("skip", $this->annotations['group']))) { + // TODO remove elseif when group skip is no longer allowed + if (array_key_exists('skip', $this->annotations)) { + return true; + } elseif (array_key_exists('group', $this->annotations) && (in_array("skip", $this->annotations['group']))) { + print("Use of group skip will be deprecated in MFTF 3.0.0. Please update tests to use skip tags."); return true; } return false; diff --git a/src/Magento/FunctionalTestingFramework/Test/Util/AnnotationExtractor.php b/src/Magento/FunctionalTestingFramework/Test/Util/AnnotationExtractor.php index 1718bf059..1f58c8e23 100644 --- a/src/Magento/FunctionalTestingFramework/Test/Util/AnnotationExtractor.php +++ b/src/Magento/FunctionalTestingFramework/Test/Util/AnnotationExtractor.php @@ -44,6 +44,7 @@ public function __construct() * @param array $testAnnotations * @param string $filename * @return array + * @throws XmlException */ public function extractAnnotations($testAnnotations, $filename) { @@ -61,10 +62,14 @@ public function extractAnnotations($testAnnotations, $filename) ); continue; } - + if ($annotationKey == "skip") { + $annotationData = $annotationData['issueId']; + $this->validateSkippedIssues($annotationData, $filename); + } foreach ($annotationData as $annotationValue) { $annotationValues[] = $annotationValue[self::ANNOTATION_VALUE]; } + $annotationObjects[$annotationKey] = $annotationValues; } $this->addStoryTitleToMap($annotationObjects, $filename); @@ -113,6 +118,22 @@ public function validateStoryTitleUniqueness() } } + /** + * Validates that all issueId tags contain a non-empty value + * @param array $issues + * @param string $filename + * @throws XmlException + * @return void + */ + public function validateSkippedIssues($issues, $filename) + { + foreach ($issues as $issueId) + if (empty($issueId['value'])) { + $message = "issueId for skipped tests cannot be empty. Test: $filename"; + throw new XmlException($message); + } + } + /** * This method transforms Magento severity values from Severity annotation * Returns Allure annotation value diff --git a/src/Magento/FunctionalTestingFramework/Test/etc/mergedTestSchema.xsd b/src/Magento/FunctionalTestingFramework/Test/etc/mergedTestSchema.xsd index 0f8aae936..b65843bae 100644 --- a/src/Magento/FunctionalTestingFramework/Test/etc/mergedTestSchema.xsd +++ b/src/Magento/FunctionalTestingFramework/Test/etc/mergedTestSchema.xsd @@ -29,6 +29,7 @@ <xs:element type="annotationType" name="testCaseId" minOccurs="0" maxOccurs="unbounded"/> <xs:element type="annotationType" name="useCaseId" minOccurs="0" maxOccurs="unbounded"/> <xs:element type="annotationType" name="group" minOccurs="0" maxOccurs="unbounded"/> + <xs:element type="skipType" name="skip" minOccurs="0" maxOccurs="unbounded"/> <xs:element type="annotationType" name="return" minOccurs="0" maxOccurs="unbounded"/> </xs:choice> </xs:complexType> @@ -55,6 +56,18 @@ </xs:extension> </xs:simpleContent> </xs:complexType> + <xs:complexType name="skipType"> + <xs:sequence maxOccurs="unbounded"> + <xs:element type="issueType" name="issueId" maxOccurs="unbounded"/> + </xs:sequence> + </xs:complexType> + <xs:complexType name="issueType"> + <xs:simpleContent> + <xs:extension base="xs:string"> + <xs:attribute type="xs:string" name="value" use="required"/> + </xs:extension> + </xs:simpleContent> + </xs:complexType> <xs:complexType name="hookType"> <xs:choice minOccurs="0" maxOccurs="unbounded"> <xs:group ref="testTypeTags"/> diff --git a/src/Magento/FunctionalTestingFramework/Util/TestGenerator.php b/src/Magento/FunctionalTestingFramework/Util/TestGenerator.php index 022ccefc4..ace2380de 100644 --- a/src/Magento/FunctionalTestingFramework/Util/TestGenerator.php +++ b/src/Magento/FunctionalTestingFramework/Util/TestGenerator.php @@ -437,6 +437,12 @@ private function generateClassAnnotations($annotationType, $annotationName) $annotationToAppend .= sprintf(" * @group %s\n", $group); } break; + + case "skip": + foreach ($annotationName as $issue) { + $annotationToAppend .= sprintf(" * @skipIssueId %s\n", $issue); + } + break; } return $annotationToAppend; From b4bdf771a8fb7438a067986e21f6c51f677ca82b Mon Sep 17 00:00:00 2001 From: Alex Calandra <acalandra@magento.com> Date: Fri, 22 Jun 2018 13:12:58 -0500 Subject: [PATCH 071/111] MQE-1068: Require Issue ID for Skipped Test - Moved deprecation warning --- .../Test/Objects/TestObject.php | 1 - .../Test/Util/AnnotationExtractor.php | 11 +++++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/src/Magento/FunctionalTestingFramework/Test/Objects/TestObject.php b/src/Magento/FunctionalTestingFramework/Test/Objects/TestObject.php index c486808f2..243565321 100644 --- a/src/Magento/FunctionalTestingFramework/Test/Objects/TestObject.php +++ b/src/Magento/FunctionalTestingFramework/Test/Objects/TestObject.php @@ -130,7 +130,6 @@ public function isSkipped() if (array_key_exists('skip', $this->annotations)) { return true; } elseif (array_key_exists('group', $this->annotations) && (in_array("skip", $this->annotations['group']))) { - print("Use of group skip will be deprecated in MFTF 3.0.0. Please update tests to use skip tags."); return true; } return false; diff --git a/src/Magento/FunctionalTestingFramework/Test/Util/AnnotationExtractor.php b/src/Magento/FunctionalTestingFramework/Test/Util/AnnotationExtractor.php index 1f58c8e23..81f7e3335 100644 --- a/src/Magento/FunctionalTestingFramework/Test/Util/AnnotationExtractor.php +++ b/src/Magento/FunctionalTestingFramework/Test/Util/AnnotationExtractor.php @@ -62,14 +62,25 @@ public function extractAnnotations($testAnnotations, $filename) ); continue; } + if ($annotationKey == "skip") { $annotationData = $annotationData['issueId']; $this->validateSkippedIssues($annotationData, $filename); } + foreach ($annotationData as $annotationValue) { $annotationValues[] = $annotationValue[self::ANNOTATION_VALUE]; } + if ($annotationKey == "group" && in_array("skip", $annotationValues)) { + print( + "Test: $filename" . + PHP_EOL . + "Use of group skip will be deprecated in MFTF 3.0.0. Please update tests to use skip tags." . + PHP_EOL + ); + } + $annotationObjects[$annotationKey] = $annotationValues; } $this->addStoryTitleToMap($annotationObjects, $filename); From 7a4fa48712ce4104cdfddc8f22a9644c347bc5cf Mon Sep 17 00:00:00 2001 From: Alex Calandra <acalandra@magento.com> Date: Fri, 22 Jun 2018 13:45:59 -0500 Subject: [PATCH 072/111] MQE-1068: Require Issue ID for Skipped Test - Made TODO messages more informative --- .../FunctionalTestingFramework/Test/Objects/TestObject.php | 2 +- .../Test/Util/AnnotationExtractor.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Magento/FunctionalTestingFramework/Test/Objects/TestObject.php b/src/Magento/FunctionalTestingFramework/Test/Objects/TestObject.php index 243565321..0fc8f2c86 100644 --- a/src/Magento/FunctionalTestingFramework/Test/Objects/TestObject.php +++ b/src/Magento/FunctionalTestingFramework/Test/Objects/TestObject.php @@ -126,7 +126,7 @@ public function getParentName() */ public function isSkipped() { - // TODO remove elseif when group skip is no longer allowed + // TODO deprecation|deprecate MFTF 3.0.0 remove elseif when group skip is no longer allowed if (array_key_exists('skip', $this->annotations)) { return true; } elseif (array_key_exists('group', $this->annotations) && (in_array("skip", $this->annotations['group']))) { diff --git a/src/Magento/FunctionalTestingFramework/Test/Util/AnnotationExtractor.php b/src/Magento/FunctionalTestingFramework/Test/Util/AnnotationExtractor.php index 81f7e3335..ab032d536 100644 --- a/src/Magento/FunctionalTestingFramework/Test/Util/AnnotationExtractor.php +++ b/src/Magento/FunctionalTestingFramework/Test/Util/AnnotationExtractor.php @@ -71,7 +71,7 @@ public function extractAnnotations($testAnnotations, $filename) foreach ($annotationData as $annotationValue) { $annotationValues[] = $annotationValue[self::ANNOTATION_VALUE]; } - + // TODO deprecation|deprecate MFTF 3.0.0 if ($annotationKey == "group" && in_array("skip", $annotationValues)) { print( "Test: $filename" . From 8811985c233ae0f44b8b00344cb158966d953473 Mon Sep 17 00:00:00 2001 From: Alex Calandra <acalandra@magento.com> Date: Fri, 22 Jun 2018 14:10:22 -0500 Subject: [PATCH 073/111] MQE-1060: Allow magentoCLI Action To Run Without Manipulating Command That Is Sent To CLI - Added CLI Paths to bootstrap for earlier decoupling story --- dev/tests/functional/_bootstrap.php | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/dev/tests/functional/_bootstrap.php b/dev/tests/functional/_bootstrap.php index 3a5e73310..51dcf2063 100755 --- a/dev/tests/functional/_bootstrap.php +++ b/dev/tests/functional/_bootstrap.php @@ -29,4 +29,13 @@ foreach ($_ENV as $key => $var) { defined($key) || define($key, $var); } + + defined('MAGENTO_CLI_COMMAND_PATH') || define( + 'MAGENTO_CLI_COMMAND_PATH', + 'dev/tests/acceptance/utils/command.php' + ); + $env->setEnvironmentVariable('MAGENTO_CLI_COMMAND_PATH', MAGENTO_CLI_COMMAND_PATH); + + defined('MAGENTO_CLI_COMMAND_PARAMETER') || define('MAGENTO_CLI_COMMAND_PARAMETER', 'command'); + $env->setEnvironmentVariable('MAGENTO_CLI_COMMAND_PARAMETER', MAGENTO_CLI_COMMAND_PARAMETER); } From de1583f6c4ab27692f101de442dd99e7f69d30dc Mon Sep 17 00:00:00 2001 From: Kevin Kozan <kkozan@magento.com> Date: Mon, 25 Jun 2018 08:12:37 -0500 Subject: [PATCH 074/111] MQE-1033: Validate duplicate element names in the same file (Section, Data, Metadata, Page) (#153) - Dom.php classes for all test objects have been created and configured. - Above classes now validate uniqueness of certain elements by their keys (ex. <entity><data key="") --- .../DuplicateNodeValidationUtilTest.php | 50 +++++++ etc/di.xml | 10 +- .../Config/MftfDom.php | 62 ++++++++ .../DataGenerator/Config/Dom.php | 123 ++++++---------- .../DataGenerator/Config/OperationDom.php | 110 +++++++++++++++ .../Config/Reader/Filesystem.php | 81 ----------- .../DataGenerator/etc/dataOperation.xsd | 1 + .../DataGenerator/etc/dataProfileSchema.xsd | 1 + .../Page/Config/Dom.php | 40 ++++-- .../Page/Config/SectionDom.php | 78 +++++++++++ .../Page/etc/PageObject.xsd | 1 + .../Page/etc/SectionObject.xsd | 1 + .../Test/Config/ActionGroupDom.php | 33 ++++- .../Test/Config/Dom.php | 132 ++++++++---------- .../DuplicateNodeValidationUtil.php | 79 +++++++++++ 15 files changed, 542 insertions(+), 260 deletions(-) create mode 100644 dev/tests/unit/Magento/FunctionalTestFramework/Util/Validation/DuplicateNodeValidationUtilTest.php create mode 100644 src/Magento/FunctionalTestingFramework/Config/MftfDom.php create mode 100644 src/Magento/FunctionalTestingFramework/DataGenerator/Config/OperationDom.php delete mode 100644 src/Magento/FunctionalTestingFramework/DataGenerator/Config/Reader/Filesystem.php create mode 100644 src/Magento/FunctionalTestingFramework/Page/Config/SectionDom.php create mode 100644 src/Magento/FunctionalTestingFramework/Util/Validation/DuplicateNodeValidationUtil.php diff --git a/dev/tests/unit/Magento/FunctionalTestFramework/Util/Validation/DuplicateNodeValidationUtilTest.php b/dev/tests/unit/Magento/FunctionalTestFramework/Util/Validation/DuplicateNodeValidationUtilTest.php new file mode 100644 index 000000000..b2358e27d --- /dev/null +++ b/dev/tests/unit/Magento/FunctionalTestFramework/Util/Validation/DuplicateNodeValidationUtilTest.php @@ -0,0 +1,50 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace tests\unit\Magento\FunctionalTestFramework\Test\Util; + +use Magento\FunctionalTestingFramework\Exceptions\Collector\ExceptionCollector; +use Magento\FunctionalTestingFramework\Exceptions\XmlException; +use Magento\FunctionalTestingFramework\Util\Validation\DuplicateNodeValidationUtil; +use Magento\FunctionalTestingFramework\Util\Validation\NameValidationUtil; +use Magento\FunctionalTestingFramework\Util\MagentoTestCase; + +class DuplicateNodeValidationUtilTest extends MagentoTestCase +{ + /** + * Validate Util flags duplicates in unique Identifiers + */ + public function testTestActionValidation() + { + // Test Data + $xml = '<tests> + <test name="test"> + <comment userInput="input1" stepKey="key1"/> + <comment userInput="input2" stepKey="key1"/> + </test> + </tests> + '; + $uniqueIdentifier = "stepKey"; + $filename = "file"; + + // Perform Test + $dom = new \DOMDocument(); + $dom->loadXML($xml); + $testNode = $dom->getElementsByTagName('test')->item(0); + + $exceptionCollector = new ExceptionCollector(); + $validator = new DuplicateNodeValidationUtil($uniqueIdentifier, $exceptionCollector); + $validator->validateChildUniqueness( + $testNode, + $filename, + $uniqueIdentifier, + $exceptionCollector + ); + $this->expectException(\Exception::class); + $exceptionCollector->throwException(); + + } +} diff --git a/etc/di.xml b/etc/di.xml index e230bacce..4f9f851b3 100644 --- a/etc/di.xml +++ b/etc/di.xml @@ -92,11 +92,12 @@ <argument name="defaultScope" xsi:type="string">Page</argument> </arguments> </virtualType> - <virtualType name="Magento\FunctionalTestingFramework\Config\Reader\Section" type="Magento\FunctionalTestingFramework\Config\Reader\Filesystem"> + <virtualType name="Magento\FunctionalTestingFramework\Config\Reader\Section" type="Magento\FunctionalTestingFramework\Config\Reader\MftfFilesystem"> <arguments> <argument name="fileResolver" xsi:type="object">Magento\FunctionalTestingFramework\Config\FileResolver\Module</argument> <argument name="converter" xsi:type="object">Magento\FunctionalTestingFramework\Config\Converter</argument> <argument name="schemaLocator" xsi:type="object">Magento\FunctionalTestingFramework\Config\SchemaLocator\Section</argument> + <argument name="domDocumentClass" xsi:type="string">Magento\FunctionalTestingFramework\Page\Config\SectionDom</argument> <argument name="idAttributes" xsi:type="array"> <item name="/sections/section" xsi:type="string">name</item> <item name="/sections/section/element" xsi:type="string">name</item> @@ -147,7 +148,7 @@ <argument name="schemaPath" xsi:type="string">Magento/FunctionalTestingFramework/DataGenerator/etc/dataProfileSchema.xsd</argument> </arguments> </virtualType> - <virtualType name="Magento\FunctionalTestingFramework\Config\Reader\DataProfile" type="Magento\FunctionalTestingFramework\DataGenerator\Config\Reader\Filesystem"> + <virtualType name="Magento\FunctionalTestingFramework\Config\Reader\DataProfile" type="Magento\FunctionalTestingFramework\Config\Reader\MftfFilesystem"> <arguments> <argument name="fileResolver" xsi:type="object">Magento\FunctionalTestingFramework\Config\FileResolver\Module</argument> <argument name="converter" xsi:type="object">Magento\FunctionalTestingFramework\Config\Converter</argument> @@ -156,6 +157,7 @@ <argument name="idAttributes" xsi:type="array"> <item name="/entities/entity" xsi:type="string">name</item> <item name="/entities/entity/(data|array)" xsi:type="string">key</item> + <item name="/entities/entity/requiredEntity" xsi:type="string">type</item> </argument> <argument name="mergeablePaths" xsi:type="array"> <item name="/entities/entity/requiredEntity" xsi:type="string"/> @@ -183,11 +185,11 @@ <argument name="schemaPath" xsi:type="string">Magento/FunctionalTestingFramework/DataGenerator/etc/dataOperation.xsd</argument> </arguments> </virtualType> - <virtualType name="Magento\FunctionalTestingFramework\Config\Reader\Metadata" type="Magento\FunctionalTestingFramework\DataGenerator\Config\Reader\Filesystem"> + <virtualType name="Magento\FunctionalTestingFramework\Config\Reader\Metadata" type="Magento\FunctionalTestingFramework\Config\Reader\MftfFilesystem"> <arguments> <argument name="fileResolver" xsi:type="object">Magento\FunctionalTestingFramework\Config\FileResolver\Module</argument> <argument name="converter" xsi:type="object">Magento\FunctionalTestingFramework\Config\Converter</argument> - <argument name="domDocumentClass" xsi:type="string">Magento\FunctionalTestingFramework\DataGenerator\Config\Dom</argument> + <argument name="domDocumentClass" xsi:type="string">Magento\FunctionalTestingFramework\DataGenerator\Config\OperationDom</argument> <argument name="schemaLocator" xsi:type="object">Magento\FunctionalTestingFramework\Config\SchemaLocator\Metadata</argument> <argument name="idAttributes" xsi:type="array"> <item name="/operations/operation" xsi:type="string">name</item> diff --git a/src/Magento/FunctionalTestingFramework/Config/MftfDom.php b/src/Magento/FunctionalTestingFramework/Config/MftfDom.php new file mode 100644 index 000000000..315db4fb7 --- /dev/null +++ b/src/Magento/FunctionalTestingFramework/Config/MftfDom.php @@ -0,0 +1,62 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\FunctionalTestingFramework\Config; + +use Magento\FunctionalTestingFramework\Exceptions\Collector\ExceptionCollector; +use Magento\FunctionalTestingFramework\Exceptions\XmlException; +use Magento\FunctionalTestingFramework\Config\Dom\NodeMergingConfig; +use Magento\FunctionalTestingFramework\Config\Dom\NodePathMatcher; +use Magento\FunctionalTestingFramework\Test\Objects\ActionObject; +use Magento\FunctionalTestingFramework\Util\Validation\DuplicateNodeValidationUtil; + +/** + * Class MftfDom + * @package Magento\FunctionalTestingFramework\Config + */ +class MftfDom extends \Magento\FunctionalTestingFramework\Config\Dom +{ + /** + * MftfDom constructor. + * @param string $xml + * @param string $filename + * @param ExceptionCollector $exceptionCollector + * @param array $idAttributes + * @param string $typeAttributeName + * @param string $schemaFile + * @param string $errorFormat + */ + public function __construct( + $xml, + $filename, + $exceptionCollector, + array $idAttributes = [], + $typeAttributeName = null, + $schemaFile = null, + $errorFormat = self::ERROR_FORMAT_DEFAULT + ) { + $this->schemaFile = $schemaFile; + $this->nodeMergingConfig = new NodeMergingConfig(new NodePathMatcher(), $idAttributes); + $this->typeAttributeName = $typeAttributeName; + $this->errorFormat = $errorFormat; + $this->dom = $this->initDom($xml, $filename, $exceptionCollector); + $this->rootNamespace = $this->dom->lookupNamespaceUri($this->dom->namespaceURI); + } + + /** + * Redirects any merges into the init method for appending xml filename + * + * @param string $xml + * @param string|null $filename + * @param ExceptionCollector $exceptionCollector + * @return void + */ + public function merge($xml, $filename = null, $exceptionCollector = null) + { + $dom = $this->initDom($xml, $filename, $exceptionCollector); + $this->mergeNode($dom->documentElement, ''); + } +} diff --git a/src/Magento/FunctionalTestingFramework/DataGenerator/Config/Dom.php b/src/Magento/FunctionalTestingFramework/DataGenerator/Config/Dom.php index ab794d5ef..683b6fdda 100644 --- a/src/Magento/FunctionalTestingFramework/DataGenerator/Config/Dom.php +++ b/src/Magento/FunctionalTestingFramework/DataGenerator/Config/Dom.php @@ -7,117 +7,78 @@ use Magento\FunctionalTestingFramework\Config\Dom\NodeMergingConfig; use Magento\FunctionalTestingFramework\Config\Dom\NodePathMatcher; +use Magento\FunctionalTestingFramework\Exceptions\Collector\ExceptionCollector; +use Magento\FunctionalTestingFramework\Util\Validation\DuplicateNodeValidationUtil; /** - * Magento configuration XML DOM utility + * MFTF actionGroup.xml configuration XML DOM utility + * @package Magento\FunctionalTestingFramework\DataGenerator\Config */ -class Dom extends \Magento\FunctionalTestingFramework\Config\Dom +class Dom extends \Magento\FunctionalTestingFramework\Config\MftfDom { + const DATA_FILE_NAME_ENDING = "Data"; + const DATA_META_FILENAME_ATTRIBUTE = "filename"; /** - * Array of non keyed mergeable paths - * - * @var array + * NodeValidationUtil + * @var DuplicateNodeValidationUtil */ - private $mergeablePaths; + private $validationUtil; /** - * Build DOM with initial XML contents and specifying identifier attributes for merging. Overridden to include new - * mergeablePaths argument which can be matched for non keyed mergeable xml elements. - * - * Format of $idAttributes: array('/xpath/to/some/node' => 'id_attribute_name') - * The path to ID attribute name should not include any attribute notations or modifiers -- only node names - * + * Entity Dom constructor. * @param string $xml + * @param string $filename + * @param ExceptionCollector $exceptionCollector * @param array $idAttributes - * @param array $mergeablePaths * @param string $typeAttributeName * @param string $schemaFile * @param string $errorFormat */ public function __construct( $xml, + $filename, + $exceptionCollector, array $idAttributes = [], - array $mergeablePaths = [], $typeAttributeName = null, $schemaFile = null, $errorFormat = self::ERROR_FORMAT_DEFAULT ) { - $this->schemaFile = $schemaFile; - $this->nodeMergingConfig = new NodeMergingConfig(new NodePathMatcher(), $idAttributes); - $this->mergeablePaths = $mergeablePaths; - $this->typeAttributeName = $typeAttributeName; - $this->errorFormat = $errorFormat; - $this->dom = $this->initDom($xml); - $this->rootNamespace = $this->dom->lookupNamespaceUri($this->dom->namespaceURI); - } - - /** - * Recursive merging of the \DOMElement into the original document. Overridden to include a call to - * - * Algorithm: - * 1. Find the same node in original document - * 2. Extend and override original document node attributes and scalar value if found - * 3. Append new node if original document doesn't have the same node - * - * @param \DOMElement $node - * @param string $parentPath path to parent node - * @return void - */ - public function mergeNode(\DOMElement $node, $parentPath) - { - $path = $this->getNodePathByParent($node, $parentPath); - $isMergeablePath = $this->validateIsPathMergeable($path); - - $matchedNode = $this->getMatchedNode($path, $isMergeablePath); - - /* Update matched node attributes and value */ - if ($matchedNode && !$isMergeablePath) { - //different node type - $this->mergeMatchingNode($node, $parentPath, $matchedNode, $path); - } else { - /* Add node as is to the document under the same parent element */ - $parentMatchedNode = $this->getMatchedNode($parentPath); - $newNode = $this->dom->importNode($node, true); - $parentMatchedNode->appendChild($newNode); - } + $this->validationUtil = new DuplicateNodeValidationUtil('key', $exceptionCollector); + parent::__construct( + $xml, + $filename, + $exceptionCollector, + $idAttributes, + $typeAttributeName, + $schemaFile, + $errorFormat + ); } /** - * Getter for node by path, overridden to include validation flag for mergeable entries - * An exception is possible if original document contains multiple nodes for identifier + * Takes a dom element from xml and appends the filename based on location * - * @param string $nodePath - * @param boolean $isMergeablePath - * @throws \Exception - * @return \DOMElement|null + * @param string $xml + * @param string|null $filename + * @return \DOMDocument */ - public function getMatchedNode($nodePath, $isMergeablePath = false) + public function initDom($xml, $filename = null) { - $xPath = new \DOMXPath($this->dom); - if ($this->rootNamespace) { - $xPath->registerNamespace(self::ROOT_NAMESPACE_PREFIX, $this->rootNamespace); - } - $matchedNodes = $xPath->query($nodePath); - $node = null; + $dom = parent::initDom($xml); - if ($matchedNodes->length > 1 && !$isMergeablePath) { - throw new \Exception("More than one node matching the query: {$nodePath}"); - } elseif ($matchedNodes->length == 1) { - $node = $matchedNodes->item(0); + if (strpos($filename, self::DATA_FILE_NAME_ENDING)) { + $entityNodes = $dom->getElementsByTagName('entity'); + foreach ($entityNodes as $entityNode) { + /** @var \DOMElement $entityNode */ + $entityNode->setAttribute(self::DATA_META_FILENAME_ATTRIBUTE, $filename); + $this->validationUtil->validateChildUniqueness( + $entityNode, + $filename + ); + } } - return $node; - } - /** - * Function which simplifies and xpath match in dom and compares with listed known mergeable paths - * - * @param string $path - * @return boolean - */ - private function validateIsPathMergeable($path) - { - $simplifiedPath = $this->nodeMergingConfig->getNodePathMatcher()->simplifyXpath($path); - return array_key_exists($simplifiedPath, $this->mergeablePaths); + return $dom; } } diff --git a/src/Magento/FunctionalTestingFramework/DataGenerator/Config/OperationDom.php b/src/Magento/FunctionalTestingFramework/DataGenerator/Config/OperationDom.php new file mode 100644 index 000000000..036bca53c --- /dev/null +++ b/src/Magento/FunctionalTestingFramework/DataGenerator/Config/OperationDom.php @@ -0,0 +1,110 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\FunctionalTestingFramework\DataGenerator\Config; + +use Magento\FunctionalTestingFramework\Config\Dom\NodeMergingConfig; +use Magento\FunctionalTestingFramework\Config\Dom\NodePathMatcher; +use Magento\FunctionalTestingFramework\Exceptions\Collector\ExceptionCollector; +use Magento\FunctionalTestingFramework\Util\Validation\DuplicateNodeValidationUtil; + +/** + * MFTF metadata.xml configuration XML DOM utility + * @package Magento\FunctionalTestingFramework\DataGenerator\Config + */ +class OperationDom extends \Magento\FunctionalTestingFramework\Config\MftfDom +{ + const METADATA_FILE_NAME_ENDING = "meta"; + const METADATA_META_FILENAME_ATTRIBUTE = "filename"; + + /** + * NodeValidationUtil + * @var DuplicateNodeValidationUtil + */ + private $validationUtil; + + /** + * Metadata Dom constructor. + * @param string $xml + * @param string $filename + * @param ExceptionCollector $exceptionCollector + * @param array $idAttributes + * @param string $typeAttributeName + * @param string $schemaFile + * @param string $errorFormat + */ + public function __construct( + $xml, + $filename, + $exceptionCollector, + array $idAttributes = [], + $typeAttributeName = null, + $schemaFile = null, + $errorFormat = self::ERROR_FORMAT_DEFAULT + ) { + $this->validationUtil = new DuplicateNodeValidationUtil('key', $exceptionCollector); + parent::__construct( + $xml, + $filename, + $exceptionCollector, + $idAttributes, + $typeAttributeName, + $schemaFile, + $errorFormat + ); + } + + /** + * Takes a dom element from xml and appends the filename based on location + * + * @param string $xml + * @param string|null $filename + * @return \DOMDocument + */ + public function initDom($xml, $filename = null) + { + $dom = parent::initDom($xml); + + if (strpos($filename, self::METADATA_FILE_NAME_ENDING)) { + $operationNodes = $dom->getElementsByTagName('operation'); + foreach ($operationNodes as $operationNode) { + /** @var \DOMElement $operationNode */ + $operationNode->setAttribute(self::METADATA_META_FILENAME_ATTRIBUTE, $filename); + $this->validateOperationElements( + $operationNode, + $filename + ); + } + } + + return $dom; + } + + /** + * Recurse through child elements and validate uniqueKeys + * @param \DOMElement $parentNode + * @param string $filename + * @return void + */ + public function validateOperationElements(\DOMElement $parentNode, $filename) + { + $this->validationUtil->validateChildUniqueness( + $parentNode, + $filename + ); + $childNodes = $parentNode->childNodes; + + for ($i = 0; $i < $childNodes->length; $i++) { + $currentNode = $childNodes->item($i); + if (!is_a($currentNode, \DOMElement::class)) { + continue; + } + $this->validateOperationElements( + $currentNode, + $filename + ); + } + } +} diff --git a/src/Magento/FunctionalTestingFramework/DataGenerator/Config/Reader/Filesystem.php b/src/Magento/FunctionalTestingFramework/DataGenerator/Config/Reader/Filesystem.php deleted file mode 100644 index 4af2f8cf4..000000000 --- a/src/Magento/FunctionalTestingFramework/DataGenerator/Config/Reader/Filesystem.php +++ /dev/null @@ -1,81 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\FunctionalTestingFramework\DataGenerator\Config\Reader; - -/** - * Filesystem configuration loader. Loads configuration from XML files, split by scopes. - */ -class Filesystem extends \Magento\FunctionalTestingFramework\Config\Reader\Filesystem -{ - /** - * An array of paths which do have a key for merging but instead are value based nodes which can only be appended - * - * @var array - */ - private $mergeablePaths; - - /** - * Constructor - * - * @param \Magento\FunctionalTestingFramework\Config\FileResolverInterface $fileResolver - * @param \Magento\FunctionalTestingFramework\Config\ConverterInterface $converter - * @param \Magento\FunctionalTestingFramework\Config\SchemaLocatorInterface $schemaLocator - * @param \Magento\FunctionalTestingFramework\Config\ValidationStateInterface $validationState - * @param string $fileName - * @param array $idAttributes - * @param array $mergeablePaths - * @param string $domDocumentClass - * @param string $defaultScope - */ - public function __construct( - \Magento\FunctionalTestingFramework\Config\FileResolverInterface $fileResolver, - \Magento\FunctionalTestingFramework\Config\ConverterInterface $converter, - \Magento\FunctionalTestingFramework\Config\SchemaLocatorInterface $schemaLocator, - \Magento\FunctionalTestingFramework\Config\ValidationStateInterface $validationState, - $fileName, - $idAttributes = [], - $mergeablePaths = [], - $domDocumentClass = \Magento\FunctionalTestingFramework\Config\Dom::class, - $defaultScope = 'global' - ) { - $this->fileResolver = $fileResolver; - $this->converter = $converter; - $this->fileName = $fileName; - $this->idAttributes = array_replace($this->idAttributes, $idAttributes); - $this->mergeablePaths = $mergeablePaths; - $this->validationState = $validationState; - $this->schemaFile = $schemaLocator->getSchema(); - $this->perFileSchema = $schemaLocator->getPerFileSchema() && $validationState->isValidationRequired() - ? $schemaLocator->getPerFileSchema() : null; - $this->domDocumentClass = $domDocumentClass; - $this->defaultScope = $defaultScope; - } - - /** - * Return newly created instance of a config merger. Overridden to include new arg in mergerClass. - * - * @param string $mergerClass - * @param string $initialContents - * @return \Magento\FunctionalTestingFramework\Config\Dom - * @throws \UnexpectedValueException - */ - protected function createConfigMerger($mergerClass, $initialContents) - { - $result = new $mergerClass( - $initialContents, - $this->idAttributes, - $this->mergeablePaths, - null, - $this->perFileSchema - ); - if (!$result instanceof \Magento\FunctionalTestingFramework\Config\Dom) { - throw new \UnexpectedValueException( - "Instance of the DOM config merger is expected, got {$mergerClass} instead." - ); - } - return $result; - } -} diff --git a/src/Magento/FunctionalTestingFramework/DataGenerator/etc/dataOperation.xsd b/src/Magento/FunctionalTestingFramework/DataGenerator/etc/dataOperation.xsd index 4d2a93c8f..7a455dc2b 100644 --- a/src/Magento/FunctionalTestingFramework/DataGenerator/etc/dataOperation.xsd +++ b/src/Magento/FunctionalTestingFramework/DataGenerator/etc/dataOperation.xsd @@ -29,6 +29,7 @@ <xs:attribute type="xs:string" name="method"/> <xs:attribute type="xs:string" name="successRegex"/> <xs:attribute type="xs:string" name="returnRegex"/> + <xs:attribute type="xs:string" name="filename"/> </xs:complexType> </xs:element> </xs:sequence> diff --git a/src/Magento/FunctionalTestingFramework/DataGenerator/etc/dataProfileSchema.xsd b/src/Magento/FunctionalTestingFramework/DataGenerator/etc/dataProfileSchema.xsd index dbb58ed7f..aec157309 100644 --- a/src/Magento/FunctionalTestingFramework/DataGenerator/etc/dataProfileSchema.xsd +++ b/src/Magento/FunctionalTestingFramework/DataGenerator/etc/dataProfileSchema.xsd @@ -84,6 +84,7 @@ </xs:documentation> </xs:annotation> </xs:attribute> + <xs:attribute type="xs:string" name="filename"/> </xs:complexType> <xs:complexType name="dataType"> diff --git a/src/Magento/FunctionalTestingFramework/Page/Config/Dom.php b/src/Magento/FunctionalTestingFramework/Page/Config/Dom.php index 879622ced..1a4f37d4c 100644 --- a/src/Magento/FunctionalTestingFramework/Page/Config/Dom.php +++ b/src/Magento/FunctionalTestingFramework/Page/Config/Dom.php @@ -11,9 +11,16 @@ use Magento\FunctionalTestingFramework\Config\Dom\NodeMergingConfig; use Magento\FunctionalTestingFramework\Config\Dom\NodePathMatcher; use Magento\FunctionalTestingFramework\Util\ModulePathExtractor; +use Magento\FunctionalTestingFramework\Util\Validation\DuplicateNodeValidationUtil; -class Dom extends \Magento\FunctionalTestingFramework\Config\Dom +/** + * MFTF page.xml configuration XML DOM utility + * @package Magento\FunctionalTestingFramework\Page\Config + */ +class Dom extends \Magento\FunctionalTestingFramework\Config\MftfDom { + const PAGE_META_FILENAME_ATTRIBUTE = "filename"; + /** * Module Path extractor * @@ -22,7 +29,14 @@ class Dom extends \Magento\FunctionalTestingFramework\Config\Dom private $modulePathExtractor; /** - * TestDom constructor. + * NodeValidationUtil + * + * @var DuplicateNodeValidationUtil + */ + private $validationUtil; + + /** + * Page Dom constructor. * @param string $xml * @param string $filename * @param ExceptionCollector $exceptionCollector @@ -40,13 +54,17 @@ public function __construct( $schemaFile = null, $errorFormat = self::ERROR_FORMAT_DEFAULT ) { - $this->schemaFile = $schemaFile; - $this->nodeMergingConfig = new NodeMergingConfig(new NodePathMatcher(), $idAttributes); - $this->typeAttributeName = $typeAttributeName; - $this->errorFormat = $errorFormat; $this->modulePathExtractor = new ModulePathExtractor(); - $this->dom = $this->initDom($xml, $filename, $exceptionCollector); - $this->rootNamespace = $this->dom->lookupNamespaceUri($this->dom->namespaceURI); + $this->validationUtil = new DuplicateNodeValidationUtil('name', $exceptionCollector); + parent::__construct( + $xml, + $filename, + $exceptionCollector, + $idAttributes, + $typeAttributeName, + $schemaFile, + $errorFormat + ); } /** @@ -54,13 +72,14 @@ public function __construct( * * @param string $xml * @param string|null $filename - * @param ExceptionCollector $exceptionCollector * @return \DOMDocument */ - public function initDom($xml, $filename = null, $exceptionCollector = null) + public function initDom($xml, $filename = null) { $dom = parent::initDom($xml); + $pagesNode = $dom->getElementsByTagName('pages')->item(0); + $this->validationUtil->validateChildUniqueness($pagesNode, $filename); $pageNodes = $dom->getElementsByTagName('page'); $currentModule = $this->modulePathExtractor->extractModuleName($filename) . @@ -78,6 +97,7 @@ public function initDom($xml, $filename = null, $exceptionCollector = null) ); } } + $pageNode->setAttribute(self::PAGE_META_FILENAME_ATTRIBUTE, $filename); } return $dom; } diff --git a/src/Magento/FunctionalTestingFramework/Page/Config/SectionDom.php b/src/Magento/FunctionalTestingFramework/Page/Config/SectionDom.php new file mode 100644 index 000000000..d0144a9d4 --- /dev/null +++ b/src/Magento/FunctionalTestingFramework/Page/Config/SectionDom.php @@ -0,0 +1,78 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\FunctionalTestingFramework\Page\Config; + +use Magento\FunctionalTestingFramework\Config\MftfApplicationConfig; +use Magento\FunctionalTestingFramework\Exceptions\Collector\ExceptionCollector; +use Magento\FunctionalTestingFramework\Config\Dom\NodeMergingConfig; +use Magento\FunctionalTestingFramework\Config\Dom\NodePathMatcher; +use Magento\FunctionalTestingFramework\Util\ModulePathExtractor; +use Magento\FunctionalTestingFramework\Util\Validation\DuplicateNodeValidationUtil; + +/** + * MFTF section.xml configuration XML DOM utility + * @package Magento\FunctionalTestingFramework\Page\Config + */ +class SectionDom extends \Magento\FunctionalTestingFramework\Config\MftfDom +{ + const SECTION_META_FILENAME_ATTRIBUTE = "filename"; + + /** + * NodeValidationUtil + * @var DuplicateNodeValidationUtil + */ + private $validationUtil; + + /** + * Entity Dom constructor. + * @param string $xml + * @param string $filename + * @param ExceptionCollector $exceptionCollector + * @param array $idAttributes + * @param string $typeAttributeName + * @param string $schemaFile + * @param string $errorFormat + */ + public function __construct( + $xml, + $filename, + $exceptionCollector, + array $idAttributes = [], + $typeAttributeName = null, + $schemaFile = null, + $errorFormat = self::ERROR_FORMAT_DEFAULT + ) { + $this->validationUtil = new DuplicateNodeValidationUtil('name', $exceptionCollector); + parent::__construct( + $xml, + $filename, + $exceptionCollector, + $idAttributes, + $typeAttributeName, + $schemaFile, + $errorFormat + ); + } + + /** + * Takes a dom element from xml and appends the filename based on location + * + * @param string $xml + * @param string|null $filename + * @return \DOMDocument + */ + public function initDom($xml, $filename = null) + { + $dom = parent::initDom($xml); + $sectionNodes = $dom->getElementsByTagName('section'); + foreach ($sectionNodes as $sectionNode) { + $sectionNode->setAttribute(self::SECTION_META_FILENAME_ATTRIBUTE, $filename); + $this->validationUtil->validateChildUniqueness($sectionNode, $filename); + } + return $dom; + } +} diff --git a/src/Magento/FunctionalTestingFramework/Page/etc/PageObject.xsd b/src/Magento/FunctionalTestingFramework/Page/etc/PageObject.xsd index 48688f9ac..c6f399a67 100644 --- a/src/Magento/FunctionalTestingFramework/Page/etc/PageObject.xsd +++ b/src/Magento/FunctionalTestingFramework/Page/etc/PageObject.xsd @@ -65,6 +65,7 @@ <xs:attribute type="xs:boolean" name="parameterized" use="optional"/> <xs:attribute type="pageArea" name="area" use="required"/> <xs:attributeGroup ref="removeAttribute"/> + <xs:attribute type="xs:string" name="filename"/> </xs:complexType> </xs:element> diff --git a/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd b/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd index a2457f4e4..44e057369 100644 --- a/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd +++ b/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd @@ -45,6 +45,7 @@ </xs:annotation> </xs:attribute> <xs:attributeGroup ref="removeAttribute"/> + <xs:attribute type="xs:string" name="filename"/> </xs:complexType> </xs:element> diff --git a/src/Magento/FunctionalTestingFramework/Test/Config/ActionGroupDom.php b/src/Magento/FunctionalTestingFramework/Test/Config/ActionGroupDom.php index bfe1c243f..ec9b3e1a0 100644 --- a/src/Magento/FunctionalTestingFramework/Test/Config/ActionGroupDom.php +++ b/src/Magento/FunctionalTestingFramework/Test/Config/ActionGroupDom.php @@ -6,7 +6,12 @@ namespace Magento\FunctionalTestingFramework\Test\Config; use Magento\FunctionalTestingFramework\Exceptions\Collector\ExceptionCollector; +use Magento\FunctionalTestingFramework\Util\Validation\DuplicateNodeValidationUtil; +/** + * MFTF actionGroup.xml configuration XML DOM utility + * @package Magento\FunctionalTestingFramework\Test\Config + */ class ActionGroupDom extends Dom { const ACTION_GROUP_FILE_NAME_ENDING = "ActionGroup.xml"; @@ -17,10 +22,9 @@ class ActionGroupDom extends Dom * * @param string $xml * @param string|null $filename - * @param ExceptionCollector $exceptionCollector * @return \DOMDocument */ - public function initDom($xml, $filename = null, $exceptionCollector = null) + public function initDom($xml, $filename = null) { $dom = parent::initDom($xml); @@ -29,22 +33,37 @@ public function initDom($xml, $filename = null, $exceptionCollector = null) foreach ($actionGroupNodes as $actionGroupNode) { /** @var \DOMElement $actionGroupNode */ $actionGroupNode->setAttribute(self::TEST_META_FILENAME_ATTRIBUTE, $filename); - $this->validateDomStepKeys($actionGroupNode, $filename, 'Action Group', $exceptionCollector); + $this->validationUtil->validateChildUniqueness( + $actionGroupNode, + $filename + ); + $beforeNode = $actionGroupNode->getElementsByTagName('before')->item(0); + $afterNode = $actionGroupNode->getElementsByTagName('after')->item(0); + if (isset($beforeNode)) { + $this->validationUtil->validateChildUniqueness( + $beforeNode, + $filename + ); + } + if (isset($afterNode)) { + $this->validationUtil->validateChildUniqueness( + $afterNode, + $filename + ); + } if ($actionGroupNode->getAttribute(self::TEST_MERGE_POINTER_AFTER) !== "") { $this->appendMergePointerToActions( $actionGroupNode, self::TEST_MERGE_POINTER_AFTER, $actionGroupNode->getAttribute(self::TEST_MERGE_POINTER_AFTER), - $filename, - $exceptionCollector + $filename ); } elseif ($actionGroupNode->getAttribute(self::TEST_MERGE_POINTER_BEFORE) !== "") { $this->appendMergePointerToActions( $actionGroupNode, self::TEST_MERGE_POINTER_BEFORE, $actionGroupNode->getAttribute(self::TEST_MERGE_POINTER_BEFORE), - $filename, - $exceptionCollector + $filename ); } } diff --git a/src/Magento/FunctionalTestingFramework/Test/Config/Dom.php b/src/Magento/FunctionalTestingFramework/Test/Config/Dom.php index fb7b0a386..df0dbf36e 100644 --- a/src/Magento/FunctionalTestingFramework/Test/Config/Dom.php +++ b/src/Magento/FunctionalTestingFramework/Test/Config/Dom.php @@ -11,8 +11,13 @@ use Magento\FunctionalTestingFramework\Config\Dom\NodeMergingConfig; use Magento\FunctionalTestingFramework\Config\Dom\NodePathMatcher; use Magento\FunctionalTestingFramework\Test\Objects\ActionObject; +use Magento\FunctionalTestingFramework\Util\Validation\DuplicateNodeValidationUtil; -class Dom extends \Magento\FunctionalTestingFramework\Config\Dom +/** + * MFTF test.xml configuration XML DOM utility + * @package Magento\FunctionalTestingFramework\Test\Config + */ +class Dom extends \Magento\FunctionalTestingFramework\Config\MftfDom { const TEST_FILE_NAME_ENDING = 'Test'; const TEST_META_FILENAME_ATTRIBUTE = 'filename'; @@ -22,7 +27,19 @@ class Dom extends \Magento\FunctionalTestingFramework\Config\Dom const TEST_MERGE_POINTER_AFTER = "insertAfter"; /** - * TestDom constructor. + * NodeValidationUtil + * @var DuplicateNodeValidationUtil + */ + protected $validationUtil; + + /** + * ExceptionCollector + * @var ExceptionCollector + */ + private $exceptionCollector; + + /** + * Metadata Dom constructor. * @param string $xml * @param string $filename * @param ExceptionCollector $exceptionCollector @@ -40,12 +57,17 @@ public function __construct( $schemaFile = null, $errorFormat = self::ERROR_FORMAT_DEFAULT ) { - $this->schemaFile = $schemaFile; - $this->nodeMergingConfig = new NodeMergingConfig(new NodePathMatcher(), $idAttributes); - $this->typeAttributeName = $typeAttributeName; - $this->errorFormat = $errorFormat; - $this->dom = $this->initDom($xml, $filename, $exceptionCollector); - $this->rootNamespace = $this->dom->lookupNamespaceUri($this->dom->namespaceURI); + $this->validationUtil = new DuplicateNodeValidationUtil('stepKey', $exceptionCollector); + $this->exceptionCollector = $exceptionCollector; + parent::__construct( + $xml, + $filename, + $exceptionCollector, + $idAttributes, + $typeAttributeName, + $schemaFile, + $errorFormat + ); } /** @@ -53,10 +75,9 @@ public function __construct( * * @param string $xml * @param string|null $filename - * @param ExceptionCollector $exceptionCollector * @return \DOMDocument */ - public function initDom($xml, $filename = null, $exceptionCollector = null) + public function initDom($xml, $filename = null) { $dom = parent::initDom($xml); @@ -65,22 +86,39 @@ public function initDom($xml, $filename = null, $exceptionCollector = null) foreach ($testNodes as $testNode) { /** @var \DOMElement $testNode */ $testNode->setAttribute(self::TEST_META_FILENAME_ATTRIBUTE, $filename); - $this->validateDomStepKeys($testNode, $filename, 'Test', $exceptionCollector); if ($testNode->getAttribute(self::TEST_MERGE_POINTER_AFTER) !== "") { $this->appendMergePointerToActions( $testNode, self::TEST_MERGE_POINTER_AFTER, $testNode->getAttribute(self::TEST_MERGE_POINTER_AFTER), - $filename, - $exceptionCollector + $filename ); } elseif ($testNode->getAttribute(self::TEST_MERGE_POINTER_BEFORE) !== "") { $this->appendMergePointerToActions( $testNode, self::TEST_MERGE_POINTER_BEFORE, $testNode->getAttribute(self::TEST_MERGE_POINTER_BEFORE), - $filename, - $exceptionCollector + $filename + ); + } + + $this->validationUtil->validateChildUniqueness( + $testNode, + $filename + ); + $beforeNode = $testNode->getElementsByTagName('before')->item(0); + $afterNode = $testNode->getElementsByTagName('after')->item(0); + + if (isset($beforeNode)) { + $this->validationUtil->validateChildUniqueness( + $beforeNode, + $filename + ); + } + if (isset($afterNode)) { + $this->validationUtil->validateChildUniqueness( + $afterNode, + $filename ); } } @@ -89,20 +127,6 @@ public function initDom($xml, $filename = null, $exceptionCollector = null) return $dom; } - /** - * Redirects any merges into the init method for appending xml filename - * - * @param string $xml - * @param string|null $filename - * @param ExceptionCollector $exceptionCollector - * @return void - */ - public function merge($xml, $filename = null, $exceptionCollector = null) - { - $dom = $this->initDom($xml, $filename, $exceptionCollector); - $this->mergeNode($dom->documentElement, ''); - } - /** * Parses DOM Structure's actions and appends a before/after attribute along with the parent's stepkey reference. * @@ -110,10 +134,9 @@ public function merge($xml, $filename = null, $exceptionCollector = null) * @param string $insertType * @param string $insertKey * @param string $filename - * @param ExceptionCollector $exceptionCollector * @return void */ - protected function appendMergePointerToActions($testNode, $insertType, $insertKey, $filename, $exceptionCollector) + protected function appendMergePointerToActions($testNode, $insertType, $insertKey, $filename) { $childNodes = $testNode->childNodes; $previousStepKey = $insertKey; @@ -129,7 +152,7 @@ protected function appendMergePointerToActions($testNode, $insertType, $insertKe if ($currentNode->hasAttribute($insertType) && $testNode->hasAttribute($insertType)) { $errorMsg = "Actions cannot have merge pointers if contained in tests that has a merge pointer."; $errorMsg .= "\n\tstepKey: {$currentNode->getAttribute('stepKey')}\tin file: {$filename}"; - $exceptionCollector->addError($filename, $errorMsg); + $this->exceptionCollector->addError($filename, $errorMsg); } $currentNode->setAttribute($actionInsertType, $previousStepKey); $previousStepKey = $currentNode->getAttribute('stepKey'); @@ -137,49 +160,4 @@ protected function appendMergePointerToActions($testNode, $insertType, $insertKe $actionInsertType = ActionObject::MERGE_ACTION_ORDER_AFTER; } } - - /** - * Parses an individual DOM structure for repeated stepKey attributes - * - * @param \DOMElement $testNode - * @param string $filename - * @param string $type - * @param ExceptionCollector $exceptionCollector - * @return void - * @throws XmlException - */ - protected function validateDomStepKeys($testNode, $filename, $type, $exceptionCollector) - { - $childNodes = $testNode->childNodes; - - $keyValues = []; - for ($i = 0; $i < $childNodes->length; $i++) { - $currentNode = $childNodes->item($i); - - if (!is_a($currentNode, \DOMElement::class)) { - continue; - } - - if (in_array($currentNode->nodeName, self::TEST_HOOK_NAMES)) { - $this->validateDomStepKeys($currentNode, $filename, $type, $exceptionCollector); - } - - if ($currentNode->hasAttribute('stepKey')) { - $keyValues[] = $currentNode->getAttribute('stepKey'); - } - } - - $withoutDuplicates = array_unique($keyValues); - $duplicates = array_diff_assoc($keyValues, $withoutDuplicates); - - if (count($duplicates) > 0) { - $stepKeyError = ""; - foreach ($duplicates as $duplicateKey => $duplicateValue) { - $stepKeyError .= "\tstepKey: {$duplicateValue} is used more than once.\n"; - } - - $errorMsg = "{$type}s cannot use stepKey more than once.\t\n{$stepKeyError}\tin file: {$filename}"; - $exceptionCollector->addError($filename, $errorMsg); - } - } } diff --git a/src/Magento/FunctionalTestingFramework/Util/Validation/DuplicateNodeValidationUtil.php b/src/Magento/FunctionalTestingFramework/Util/Validation/DuplicateNodeValidationUtil.php new file mode 100644 index 000000000..d0352d944 --- /dev/null +++ b/src/Magento/FunctionalTestingFramework/Util/Validation/DuplicateNodeValidationUtil.php @@ -0,0 +1,79 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\FunctionalTestingFramework\Util\Validation; + +use Magento\FunctionalTestingFramework\Exceptions\Collector\ExceptionCollector; +use Magento\FunctionalTestingFramework\Exceptions\XmlException; + +/** + * Class DuplicateNodeValidationUtil + * @package Magento\FunctionalTestingFramework\Util\Validation + */ +class DuplicateNodeValidationUtil +{ + /** + * Key to use as unique identifier in validation + * @var string + */ + private $uniqueKey; + + /** + * ExceptionColletor used to catch errors. + * @var ExceptionCollector + */ + private $exceptionCollector; + + /** + * DuplicateNodeValidationUtil constructor. + * @param string $uniqueKey + * @param ExceptionCollector $exceptionCollector + */ + public function __construct($uniqueKey, $exceptionCollector) + { + $this->uniqueKey = $uniqueKey; + $this->exceptionCollector = $exceptionCollector; + } + + /** + * Parses through parent's children to find and flag duplicate values in given uniqueKey. + * + * @param \DOMElement $parentNode + * @param string $filename + * @return void + */ + public function validateChildUniqueness(\DOMElement $parentNode, $filename) + { + $childNodes = $parentNode->childNodes; + $type = ucfirst($parentNode->tagName); + + $keyValues = []; + for ($i = 0; $i < $childNodes->length; $i++) { + $currentNode = $childNodes->item($i); + + if (!is_a($currentNode, \DOMElement::class)) { + continue; + } + + if ($currentNode->hasAttribute($this->uniqueKey)) { + $keyValues[] = $currentNode->getAttribute($this->uniqueKey); + } + } + + $withoutDuplicates = array_unique($keyValues); + + if (count($withoutDuplicates) != count($keyValues)) { + $duplicates = array_diff_assoc($keyValues, $withoutDuplicates); + $keyError = ""; + foreach ($duplicates as $duplicateKey => $duplicateValue) { + $keyError .= "\t{$this->uniqueKey}: {$duplicateValue} is used more than once.\n"; + } + + $errorMsg = "{$type} cannot use {$this->uniqueKey}s more than once.\t\n{$keyError}\tin file: {$filename}"; + $this->exceptionCollector->addError($filename, $errorMsg); + } + } +} From eca4c88d46283eb2e669d7ded8c4daee801a7f9d Mon Sep 17 00:00:00 2001 From: Alex Calandra <acalandra@magento.com> Date: Mon, 25 Jun 2018 09:14:30 -0500 Subject: [PATCH 075/111] MQE-1068: Require Issue ID for Skipped Test - Added missing {} for foreach --- .../Test/Util/AnnotationExtractor.php | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/Magento/FunctionalTestingFramework/Test/Util/AnnotationExtractor.php b/src/Magento/FunctionalTestingFramework/Test/Util/AnnotationExtractor.php index ab032d536..01ea44f0d 100644 --- a/src/Magento/FunctionalTestingFramework/Test/Util/AnnotationExtractor.php +++ b/src/Magento/FunctionalTestingFramework/Test/Util/AnnotationExtractor.php @@ -138,10 +138,11 @@ public function validateStoryTitleUniqueness() */ public function validateSkippedIssues($issues, $filename) { - foreach ($issues as $issueId) - if (empty($issueId['value'])) { - $message = "issueId for skipped tests cannot be empty. Test: $filename"; - throw new XmlException($message); + foreach ($issues as $issueId) { + if (empty($issueId['value'])) { + $message = "issueId for skipped tests cannot be empty. Test: $filename"; + throw new XmlException($message); + } } } From 9f46f0af6acff78c1e12d55d675de11402305933 Mon Sep 17 00:00:00 2001 From: Alex Calandra <acalandra@magento.com> Date: Mon, 25 Jun 2018 13:08:50 -0500 Subject: [PATCH 076/111] MQE-1074: Verify MFTF Compatibility with Phpunit 7 - Updated composer json to use requirements for phpunit 7 --- composer.json | 4 +- composer.lock | 575 +++++++++++++++++++++++++++----------------------- 2 files changed, 312 insertions(+), 267 deletions(-) diff --git a/composer.json b/composer.json index 8099cd58e..2ec1c32e4 100755 --- a/composer.json +++ b/composer.json @@ -11,7 +11,7 @@ "require": { "php": "7.0.2|7.0.4|~7.0.6|~7.1.0|~7.2.0", "allure-framework/allure-codeception": "~1.2.6", - "codeception/codeception": "~2.3.4", + "codeception/codeception": "~2.4.2", "consolidation/robo": "^1.0.0", "epfremme/swagger-php": "^2.0", "flow/jsonpath": ">0.2", @@ -23,7 +23,7 @@ }, "require-dev": { "squizlabs/php_codesniffer": "1.5.3", - "sebastian/phpcpd": "~3.0", + "sebastian/phpcpd": "~4.0", "brainmaestro/composer-git-hooks": "^2.3", "doctrine/cache": "<1.7.0", "codeception/aspect-mock": "^3.0", diff --git a/composer.lock b/composer.lock index fd17255c6..c576be44c 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "723c7b1ff62d50ff5e7d9aeaeb91b77c", + "content-hash": "a572ba34cb4bddd81eaa67c23ad3e9a8", "packages": [ { "name": "allure-framework/allure-codeception", @@ -111,16 +111,16 @@ }, { "name": "behat/gherkin", - "version": "v4.4.5", + "version": "v4.5.1", "source": { "type": "git", "url": "https://github.com/Behat/Gherkin.git", - "reference": "5c14cff4f955b17d20d088dec1bde61c0539ec74" + "reference": "74ac03d52c5e23ad8abd5c5cce4ab0e8dc1b530a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Behat/Gherkin/zipball/5c14cff4f955b17d20d088dec1bde61c0539ec74", - "reference": "5c14cff4f955b17d20d088dec1bde61c0539ec74", + "url": "https://api.github.com/repos/Behat/Gherkin/zipball/74ac03d52c5e23ad8abd5c5cce4ab0e8dc1b530a", + "reference": "74ac03d52c5e23ad8abd5c5cce4ab0e8dc1b530a", "shasum": "" }, "require": { @@ -166,35 +166,32 @@ "gherkin", "parser" ], - "time": "2016-10-30T11:50:56+00:00" + "time": "2017-08-30T11:04:43+00:00" }, { "name": "codeception/codeception", - "version": "2.3.9", + "version": "2.4.2", "source": { "type": "git", "url": "https://github.com/Codeception/Codeception.git", - "reference": "104f46fa0bde339f1bcc3a375aac21eb36e65a1e" + "reference": "bc8433e3da75bc28c47df6f3afc22b9d4f65c0b4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Codeception/Codeception/zipball/104f46fa0bde339f1bcc3a375aac21eb36e65a1e", - "reference": "104f46fa0bde339f1bcc3a375aac21eb36e65a1e", + "url": "https://api.github.com/repos/Codeception/Codeception/zipball/bc8433e3da75bc28c47df6f3afc22b9d4f65c0b4", + "reference": "bc8433e3da75bc28c47df6f3afc22b9d4f65c0b4", "shasum": "" }, "require": { - "behat/gherkin": "~4.4.0", - "codeception/stub": "^1.0", + "behat/gherkin": "^4.4.0", + "codeception/phpunit-wrapper": "^6.0.9|^7.0.6", + "codeception/stub": "^2.0", "ext-json": "*", "ext-mbstring": "*", "facebook/webdriver": ">=1.1.3 <2.0", "guzzlehttp/guzzle": ">=4.1.4 <7.0", "guzzlehttp/psr7": "~1.0", - "php": ">=5.4.0 <8.0", - "phpunit/php-code-coverage": ">=2.2.4 <6.0", - "phpunit/phpunit": ">=4.8.28 <5.0.0 || >=5.6.3 <7.0", - "sebastian/comparator": ">1.1 <3.0", - "sebastian/diff": ">=1.4 <3.0", + "php": ">=5.6.0 <8.0", "symfony/browser-kit": ">=2.7 <5.0", "symfony/console": ">=2.7 <5.0", "symfony/css-selector": ">=2.7 <5.0", @@ -260,20 +257,63 @@ "functional testing", "unit testing" ], - "time": "2018-02-26T23:29:41+00:00" + "time": "2018-05-26T22:17:46+00:00" + }, + { + "name": "codeception/phpunit-wrapper", + "version": "7.1.4", + "source": { + "type": "git", + "url": "https://github.com/Codeception/phpunit-wrapper.git", + "reference": "f18ed631f1eddbb603d72219f577d223b23a1f89" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Codeception/phpunit-wrapper/zipball/f18ed631f1eddbb603d72219f577d223b23a1f89", + "reference": "f18ed631f1eddbb603d72219f577d223b23a1f89", + "shasum": "" + }, + "require": { + "phpunit/php-code-coverage": "^6.0", + "phpunit/phpunit": "^7.1", + "sebastian/comparator": "^3.0", + "sebastian/diff": "^3.0" + }, + "require-dev": { + "codeception/specify": "*", + "vlucas/phpdotenv": "^2.4" + }, + "type": "library", + "autoload": { + "psr-4": { + "Codeception\\PHPUnit\\": "src\\" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Davert", + "email": "davert.php@resend.cc" + } + ], + "description": "PHPUnit classes used by Codeception", + "time": "2018-06-20T20:07:21+00:00" }, { "name": "codeception/stub", - "version": "1.0.2", + "version": "2.0.1", "source": { "type": "git", "url": "https://github.com/Codeception/Stub.git", - "reference": "95fb7a36b81890dd2e5163e7ab31310df6f1bb99" + "reference": "b2eff325d8ff0b824ff659048be7be4e5767d7d0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Codeception/Stub/zipball/95fb7a36b81890dd2e5163e7ab31310df6f1bb99", - "reference": "95fb7a36b81890dd2e5163e7ab31310df6f1bb99", + "url": "https://api.github.com/repos/Codeception/Stub/zipball/b2eff325d8ff0b824ff659048be7be4e5767d7d0", + "reference": "b2eff325d8ff0b824ff659048be7be4e5767d7d0", "shasum": "" }, "require": { @@ -293,7 +333,7 @@ "MIT" ], "description": "Flexible Stub wrapper for PHPUnit's Mock Builder", - "time": "2018-02-18T13:56:56+00:00" + "time": "2018-05-18T14:33:08+00:00" }, { "name": "consolidation/annotated-command", @@ -809,32 +849,32 @@ }, { "name": "doctrine/instantiator", - "version": "1.0.5", + "version": "1.1.0", "source": { "type": "git", "url": "https://github.com/doctrine/instantiator.git", - "reference": "8e884e78f9f0eb1329e445619e04456e64d8051d" + "reference": "185b8868aa9bf7159f5f953ed5afb2d7fcdc3bda" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/instantiator/zipball/8e884e78f9f0eb1329e445619e04456e64d8051d", - "reference": "8e884e78f9f0eb1329e445619e04456e64d8051d", + "url": "https://api.github.com/repos/doctrine/instantiator/zipball/185b8868aa9bf7159f5f953ed5afb2d7fcdc3bda", + "reference": "185b8868aa9bf7159f5f953ed5afb2d7fcdc3bda", "shasum": "" }, "require": { - "php": ">=5.3,<8.0-DEV" + "php": "^7.1" }, "require-dev": { "athletic/athletic": "~0.1.8", "ext-pdo": "*", "ext-phar": "*", - "phpunit/phpunit": "~4.0", - "squizlabs/php_codesniffer": "~2.0" + "phpunit/phpunit": "^6.2.3", + "squizlabs/php_codesniffer": "^3.0.2" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.0.x-dev" + "dev-master": "1.2.x-dev" } }, "autoload": { @@ -859,7 +899,7 @@ "constructor", "instantiate" ], - "time": "2015-06-14T21:17:01+00:00" + "time": "2017-07-22T11:58:36+00:00" }, { "name": "doctrine/lexer", @@ -963,34 +1003,39 @@ }, { "name": "facebook/webdriver", - "version": "1.5.0", + "version": "1.6.0", "source": { "type": "git", "url": "https://github.com/facebook/php-webdriver.git", - "reference": "86b5ca2f67173c9d34340845dd690149c886a605" + "reference": "bd8c740097eb9f2fc3735250fc1912bc811a954e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/facebook/php-webdriver/zipball/86b5ca2f67173c9d34340845dd690149c886a605", - "reference": "86b5ca2f67173c9d34340845dd690149c886a605", + "url": "https://api.github.com/repos/facebook/php-webdriver/zipball/bd8c740097eb9f2fc3735250fc1912bc811a954e", + "reference": "bd8c740097eb9f2fc3735250fc1912bc811a954e", "shasum": "" }, "require": { "ext-curl": "*", + "ext-json": "*", + "ext-mbstring": "*", "ext-zip": "*", "php": "^5.6 || ~7.0", "symfony/process": "^2.8 || ^3.1 || ^4.0" }, "require-dev": { "friendsofphp/php-cs-fixer": "^2.0", - "guzzle/guzzle": "^3.4.1", - "php-coveralls/php-coveralls": "^1.0.2", + "jakub-onderka/php-parallel-lint": "^0.9.2", + "php-coveralls/php-coveralls": "^2.0", "php-mock/php-mock-phpunit": "^1.1", "phpunit/phpunit": "^5.7", "sebastian/environment": "^1.3.4 || ^2.0 || ^3.0", "squizlabs/php_codesniffer": "^2.6", "symfony/var-dumper": "^3.3 || ^4.0" }, + "suggest": { + "ext-SimpleXML": "For Firefox profile creation" + }, "type": "library", "extra": { "branch-alias": { @@ -1014,7 +1059,7 @@ "selenium", "webdriver" ], - "time": "2017-11-15T11:08:09+00:00" + "time": "2018-05-16T17:37:13+00:00" }, { "name": "flow/jsonpath", @@ -1204,16 +1249,16 @@ }, { "name": "guzzlehttp/guzzle", - "version": "6.3.0", + "version": "6.3.3", "source": { "type": "git", "url": "https://github.com/guzzle/guzzle.git", - "reference": "f4db5a78a5ea468d4831de7f0bf9d9415e348699" + "reference": "407b0cb880ace85c9b63c5f9551db498cb2d50ba" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/guzzle/zipball/f4db5a78a5ea468d4831de7f0bf9d9415e348699", - "reference": "f4db5a78a5ea468d4831de7f0bf9d9415e348699", + "url": "https://api.github.com/repos/guzzle/guzzle/zipball/407b0cb880ace85c9b63c5f9551db498cb2d50ba", + "reference": "407b0cb880ace85c9b63c5f9551db498cb2d50ba", "shasum": "" }, "require": { @@ -1223,7 +1268,7 @@ }, "require-dev": { "ext-curl": "*", - "phpunit/phpunit": "^4.0 || ^5.0", + "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.4 || ^7.0", "psr/log": "^1.0" }, "suggest": { @@ -1232,7 +1277,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "6.2-dev" + "dev-master": "6.3-dev" } }, "autoload": { @@ -1265,7 +1310,7 @@ "rest", "web service" ], - "time": "2017-06-22T18:50:49+00:00" + "time": "2018-04-22T15:46:56+00:00" }, { "name": "guzzlehttp/promises", @@ -1793,25 +1838,28 @@ }, { "name": "myclabs/deep-copy", - "version": "1.7.0", + "version": "1.8.1", "source": { "type": "git", "url": "https://github.com/myclabs/DeepCopy.git", - "reference": "3b8a3a99ba1f6a3952ac2747d989303cbd6b7a3e" + "reference": "3e01bdad3e18354c3dce54466b7fbe33a9f9f7f8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/3b8a3a99ba1f6a3952ac2747d989303cbd6b7a3e", - "reference": "3b8a3a99ba1f6a3952ac2747d989303cbd6b7a3e", + "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/3e01bdad3e18354c3dce54466b7fbe33a9f9f7f8", + "reference": "3e01bdad3e18354c3dce54466b7fbe33a9f9f7f8", "shasum": "" }, "require": { - "php": "^5.6 || ^7.0" + "php": "^7.1" + }, + "replace": { + "myclabs/deep-copy": "self.version" }, "require-dev": { "doctrine/collections": "^1.0", "doctrine/common": "^2.6", - "phpunit/phpunit": "^4.1" + "phpunit/phpunit": "^7.1" }, "type": "library", "autoload": { @@ -1834,7 +1882,7 @@ "object", "object graph" ], - "time": "2017-10-19T19:58:43+00:00" + "time": "2018-06-11T23:09:50+00:00" }, { "name": "paragonie/random_compat", @@ -2238,23 +2286,23 @@ }, { "name": "phpspec/prophecy", - "version": "1.7.5", + "version": "1.7.6", "source": { "type": "git", "url": "https://github.com/phpspec/prophecy.git", - "reference": "dfd6be44111a7c41c2e884a336cc4f461b3b2401" + "reference": "33a7e3c4fda54e912ff6338c48823bd5c0f0b712" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpspec/prophecy/zipball/dfd6be44111a7c41c2e884a336cc4f461b3b2401", - "reference": "dfd6be44111a7c41c2e884a336cc4f461b3b2401", + "url": "https://api.github.com/repos/phpspec/prophecy/zipball/33a7e3c4fda54e912ff6338c48823bd5c0f0b712", + "reference": "33a7e3c4fda54e912ff6338c48823bd5c0f0b712", "shasum": "" }, "require": { "doctrine/instantiator": "^1.0.2", "php": "^5.3|^7.0", "phpdocumentor/reflection-docblock": "^2.0|^3.0.2|^4.0", - "sebastian/comparator": "^1.1|^2.0", + "sebastian/comparator": "^1.1|^2.0|^3.0", "sebastian/recursion-context": "^1.0|^2.0|^3.0" }, "require-dev": { @@ -2297,44 +2345,44 @@ "spy", "stub" ], - "time": "2018-02-19T10:16:54+00:00" + "time": "2018-04-18T13:57:24+00:00" }, { "name": "phpunit/php-code-coverage", - "version": "5.3.0", + "version": "6.0.5", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-code-coverage.git", - "reference": "661f34d0bd3f1a7225ef491a70a020ad23a057a1" + "reference": "4cab20a326d14de7575a8e235c70d879b569a57a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/661f34d0bd3f1a7225ef491a70a020ad23a057a1", - "reference": "661f34d0bd3f1a7225ef491a70a020ad23a057a1", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/4cab20a326d14de7575a8e235c70d879b569a57a", + "reference": "4cab20a326d14de7575a8e235c70d879b569a57a", "shasum": "" }, "require": { "ext-dom": "*", "ext-xmlwriter": "*", - "php": "^7.0", + "php": "^7.1", "phpunit/php-file-iterator": "^1.4.2", "phpunit/php-text-template": "^1.2.1", - "phpunit/php-token-stream": "^2.0.1", + "phpunit/php-token-stream": "^3.0", "sebastian/code-unit-reverse-lookup": "^1.0.1", - "sebastian/environment": "^3.0", + "sebastian/environment": "^3.1", "sebastian/version": "^2.0.1", "theseer/tokenizer": "^1.1" }, "require-dev": { - "phpunit/phpunit": "^6.0" + "phpunit/phpunit": "^7.0" }, "suggest": { - "ext-xdebug": "^2.5.5" + "ext-xdebug": "^2.6.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "5.3.x-dev" + "dev-master": "6.0-dev" } }, "autoload": { @@ -2360,7 +2408,7 @@ "testing", "xunit" ], - "time": "2017-12-06T09:29:45+00:00" + "time": "2018-05-28T11:49:20+00:00" }, { "name": "phpunit/php-file-iterator", @@ -2452,28 +2500,28 @@ }, { "name": "phpunit/php-timer", - "version": "1.0.9", + "version": "2.0.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-timer.git", - "reference": "3dcf38ca72b158baf0bc245e9184d3fdffa9c46f" + "reference": "8b8454ea6958c3dee38453d3bd571e023108c91f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/3dcf38ca72b158baf0bc245e9184d3fdffa9c46f", - "reference": "3dcf38ca72b158baf0bc245e9184d3fdffa9c46f", + "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/8b8454ea6958c3dee38453d3bd571e023108c91f", + "reference": "8b8454ea6958c3dee38453d3bd571e023108c91f", "shasum": "" }, "require": { - "php": "^5.3.3 || ^7.0" + "php": "^7.1" }, "require-dev": { - "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.0" + "phpunit/phpunit": "^7.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.0-dev" + "dev-master": "2.0-dev" } }, "autoload": { @@ -2488,7 +2536,7 @@ "authors": [ { "name": "Sebastian Bergmann", - "email": "sb@sebastian-bergmann.de", + "email": "sebastian@phpunit.de", "role": "lead" } ], @@ -2497,33 +2545,33 @@ "keywords": [ "timer" ], - "time": "2017-02-26T11:10:40+00:00" + "time": "2018-02-01T13:07:23+00:00" }, { "name": "phpunit/php-token-stream", - "version": "2.0.2", + "version": "3.0.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-token-stream.git", - "reference": "791198a2c6254db10131eecfe8c06670700904db" + "reference": "21ad88bbba7c3d93530d93994e0a33cd45f02ace" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/791198a2c6254db10131eecfe8c06670700904db", - "reference": "791198a2c6254db10131eecfe8c06670700904db", + "url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/21ad88bbba7c3d93530d93994e0a33cd45f02ace", + "reference": "21ad88bbba7c3d93530d93994e0a33cd45f02ace", "shasum": "" }, "require": { "ext-tokenizer": "*", - "php": "^7.0" + "php": "^7.1" }, "require-dev": { - "phpunit/phpunit": "^6.2.4" + "phpunit/phpunit": "^7.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.0-dev" + "dev-master": "3.0-dev" } }, "autoload": { @@ -2546,20 +2594,20 @@ "keywords": [ "tokenizer" ], - "time": "2017-11-27T05:48:46+00:00" + "time": "2018-02-01T13:16:43+00:00" }, { "name": "phpunit/phpunit", - "version": "6.5.8", + "version": "7.1.5", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "4f21a3c6b97c42952fd5c2837bb354ec0199b97b" + "reference": "ca64dba53b88aba6af32aebc6b388068db95c435" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/4f21a3c6b97c42952fd5c2837bb354ec0199b97b", - "reference": "4f21a3c6b97c42952fd5c2837bb354ec0199b97b", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/ca64dba53b88aba6af32aebc6b388068db95c435", + "reference": "ca64dba53b88aba6af32aebc6b388068db95c435", "shasum": "" }, "require": { @@ -2571,15 +2619,15 @@ "myclabs/deep-copy": "^1.6.1", "phar-io/manifest": "^1.0.1", "phar-io/version": "^1.0", - "php": "^7.0", + "php": "^7.1", "phpspec/prophecy": "^1.7", - "phpunit/php-code-coverage": "^5.3", + "phpunit/php-code-coverage": "^6.0.1", "phpunit/php-file-iterator": "^1.4.3", "phpunit/php-text-template": "^1.2.1", - "phpunit/php-timer": "^1.0.9", - "phpunit/phpunit-mock-objects": "^5.0.5", - "sebastian/comparator": "^2.1", - "sebastian/diff": "^2.0", + "phpunit/php-timer": "^2.0", + "phpunit/phpunit-mock-objects": "^6.1.1", + "sebastian/comparator": "^3.0", + "sebastian/diff": "^3.0", "sebastian/environment": "^3.1", "sebastian/exporter": "^3.1", "sebastian/global-state": "^2.0", @@ -2587,16 +2635,12 @@ "sebastian/resource-operations": "^1.0", "sebastian/version": "^2.0.1" }, - "conflict": { - "phpdocumentor/reflection-docblock": "3.0.2", - "phpunit/dbunit": "<3.0" - }, "require-dev": { "ext-pdo": "*" }, "suggest": { "ext-xdebug": "*", - "phpunit/php-invoker": "^1.1" + "phpunit/php-invoker": "^2.0" }, "bin": [ "phpunit" @@ -2604,7 +2648,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "6.5.x-dev" + "dev-master": "7.1-dev" } }, "autoload": { @@ -2630,33 +2674,30 @@ "testing", "xunit" ], - "time": "2018-04-10T11:38:34+00:00" + "time": "2018-04-29T15:09:19+00:00" }, { "name": "phpunit/phpunit-mock-objects", - "version": "5.0.6", + "version": "6.1.2", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit-mock-objects.git", - "reference": "33fd41a76e746b8fa96d00b49a23dadfa8334cdf" + "reference": "f9756fd4f43f014cb2dca98deeaaa8ce5500a36e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit-mock-objects/zipball/33fd41a76e746b8fa96d00b49a23dadfa8334cdf", - "reference": "33fd41a76e746b8fa96d00b49a23dadfa8334cdf", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit-mock-objects/zipball/f9756fd4f43f014cb2dca98deeaaa8ce5500a36e", + "reference": "f9756fd4f43f014cb2dca98deeaaa8ce5500a36e", "shasum": "" }, "require": { "doctrine/instantiator": "^1.0.5", - "php": "^7.0", + "php": "^7.1", "phpunit/php-text-template": "^1.2.1", "sebastian/exporter": "^3.1" }, - "conflict": { - "phpunit/phpunit": "<6.0" - }, "require-dev": { - "phpunit/phpunit": "^6.5" + "phpunit/phpunit": "^7.0" }, "suggest": { "ext-soap": "*" @@ -2664,7 +2705,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "5.0.x-dev" + "dev-master": "6.1-dev" } }, "autoload": { @@ -2689,7 +2730,7 @@ "mock", "xunit" ], - "time": "2018-01-06T05:45:45+00:00" + "time": "2018-05-29T13:54:20+00:00" }, { "name": "psr/container", @@ -2964,30 +3005,30 @@ }, { "name": "sebastian/comparator", - "version": "2.1.3", + "version": "3.0.1", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/comparator.git", - "reference": "34369daee48eafb2651bea869b4b15d75ccc35f9" + "reference": "591a30922f54656695e59b1f39501aec513403da" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/34369daee48eafb2651bea869b4b15d75ccc35f9", - "reference": "34369daee48eafb2651bea869b4b15d75ccc35f9", + "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/591a30922f54656695e59b1f39501aec513403da", + "reference": "591a30922f54656695e59b1f39501aec513403da", "shasum": "" }, "require": { - "php": "^7.0", - "sebastian/diff": "^2.0 || ^3.0", + "php": "^7.1", + "sebastian/diff": "^3.0", "sebastian/exporter": "^3.1" }, "require-dev": { - "phpunit/phpunit": "^6.4" + "phpunit/phpunit": "^7.1" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.1.x-dev" + "dev-master": "3.0-dev" } }, "autoload": { @@ -3024,32 +3065,33 @@ "compare", "equality" ], - "time": "2018-02-01T13:46:46+00:00" + "time": "2018-06-14T15:05:28+00:00" }, { "name": "sebastian/diff", - "version": "2.0.1", + "version": "3.0.1", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/diff.git", - "reference": "347c1d8b49c5c3ee30c7040ea6fc446790e6bddd" + "reference": "366541b989927187c4ca70490a35615d3fef2dce" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/347c1d8b49c5c3ee30c7040ea6fc446790e6bddd", - "reference": "347c1d8b49c5c3ee30c7040ea6fc446790e6bddd", + "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/366541b989927187c4ca70490a35615d3fef2dce", + "reference": "366541b989927187c4ca70490a35615d3fef2dce", "shasum": "" }, "require": { - "php": "^7.0" + "php": "^7.1" }, "require-dev": { - "phpunit/phpunit": "^6.2" + "phpunit/phpunit": "^7.0", + "symfony/process": "^2 || ^3.3 || ^4" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.0-dev" + "dev-master": "3.0-dev" } }, "autoload": { @@ -3074,9 +3116,12 @@ "description": "Diff implementation", "homepage": "https://github.com/sebastianbergmann/diff", "keywords": [ - "diff" + "diff", + "udiff", + "unidiff", + "unified diff" ], - "time": "2017-08-03T08:09:46+00:00" + "time": "2018-06-10T07:54:39+00:00" }, { "name": "sebastian/environment", @@ -3478,25 +3523,25 @@ }, { "name": "symfony/browser-kit", - "version": "v3.4.6", + "version": "v4.1.1", "source": { "type": "git", "url": "https://github.com/symfony/browser-kit.git", - "reference": "490f27762705c8489bd042fe3e9377a191dba9b4" + "reference": "ff9ac5d5808a530b2e7f6abcf3a2412d4f9bcd62" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/browser-kit/zipball/490f27762705c8489bd042fe3e9377a191dba9b4", - "reference": "490f27762705c8489bd042fe3e9377a191dba9b4", + "url": "https://api.github.com/repos/symfony/browser-kit/zipball/ff9ac5d5808a530b2e7f6abcf3a2412d4f9bcd62", + "reference": "ff9ac5d5808a530b2e7f6abcf3a2412d4f9bcd62", "shasum": "" }, "require": { - "php": "^5.5.9|>=7.0.8", - "symfony/dom-crawler": "~2.8|~3.0|~4.0" + "php": "^7.1.3", + "symfony/dom-crawler": "~3.4|~4.0" }, "require-dev": { - "symfony/css-selector": "~2.8|~3.0|~4.0", - "symfony/process": "~2.8|~3.0|~4.0" + "symfony/css-selector": "~3.4|~4.0", + "symfony/process": "~3.4|~4.0" }, "suggest": { "symfony/process": "" @@ -3504,7 +3549,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "3.4-dev" + "dev-master": "4.1-dev" } }, "autoload": { @@ -3531,25 +3576,24 @@ ], "description": "Symfony BrowserKit Component", "homepage": "https://symfony.com", - "time": "2018-01-03T07:37:34+00:00" + "time": "2018-06-04T17:31:56+00:00" }, { "name": "symfony/console", - "version": "v3.4.6", + "version": "v4.1.1", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "067339e9b8ec30d5f19f5950208893ff026b94f7" + "reference": "70591cda56b4b47c55776ac78e157c4bb6c8b43f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/067339e9b8ec30d5f19f5950208893ff026b94f7", - "reference": "067339e9b8ec30d5f19f5950208893ff026b94f7", + "url": "https://api.github.com/repos/symfony/console/zipball/70591cda56b4b47c55776ac78e157c4bb6c8b43f", + "reference": "70591cda56b4b47c55776ac78e157c4bb6c8b43f", "shasum": "" }, "require": { - "php": "^5.5.9|>=7.0.8", - "symfony/debug": "~2.8|~3.0|~4.0", + "php": "^7.1.3", "symfony/polyfill-mbstring": "~1.0" }, "conflict": { @@ -3558,14 +3602,14 @@ }, "require-dev": { "psr/log": "~1.0", - "symfony/config": "~3.3|~4.0", + "symfony/config": "~3.4|~4.0", "symfony/dependency-injection": "~3.4|~4.0", - "symfony/event-dispatcher": "~2.8|~3.0|~4.0", + "symfony/event-dispatcher": "~3.4|~4.0", "symfony/lock": "~3.4|~4.0", - "symfony/process": "~3.3|~4.0" + "symfony/process": "~3.4|~4.0" }, "suggest": { - "psr/log": "For using the console logger", + "psr/log-implementation": "For using the console logger", "symfony/event-dispatcher": "", "symfony/lock": "", "symfony/process": "" @@ -3573,7 +3617,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "3.4-dev" + "dev-master": "4.1-dev" } }, "autoload": { @@ -3600,29 +3644,29 @@ ], "description": "Symfony Console Component", "homepage": "https://symfony.com", - "time": "2018-02-26T15:46:28+00:00" + "time": "2018-05-31T10:17:53+00:00" }, { "name": "symfony/css-selector", - "version": "v3.4.6", + "version": "v4.1.1", "source": { "type": "git", "url": "https://github.com/symfony/css-selector.git", - "reference": "544655f1fc078a9cd839fdda2b7b1e64627c826a" + "reference": "03ac71606ecb0b0ce792faa17d74cc32c2949ef4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/css-selector/zipball/544655f1fc078a9cd839fdda2b7b1e64627c826a", - "reference": "544655f1fc078a9cd839fdda2b7b1e64627c826a", + "url": "https://api.github.com/repos/symfony/css-selector/zipball/03ac71606ecb0b0ce792faa17d74cc32c2949ef4", + "reference": "03ac71606ecb0b0ce792faa17d74cc32c2949ef4", "shasum": "" }, "require": { - "php": "^5.5.9|>=7.0.8" + "php": "^7.1.3" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "3.4-dev" + "dev-master": "4.1-dev" } }, "autoload": { @@ -3653,84 +3697,29 @@ ], "description": "Symfony CssSelector Component", "homepage": "https://symfony.com", - "time": "2018-02-03T14:55:07+00:00" - }, - { - "name": "symfony/debug", - "version": "v3.4.6", - "source": { - "type": "git", - "url": "https://github.com/symfony/debug.git", - "reference": "9b1071f86e79e1999b3d3675d2e0e7684268b9bc" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/debug/zipball/9b1071f86e79e1999b3d3675d2e0e7684268b9bc", - "reference": "9b1071f86e79e1999b3d3675d2e0e7684268b9bc", - "shasum": "" - }, - "require": { - "php": "^5.5.9|>=7.0.8", - "psr/log": "~1.0" - }, - "conflict": { - "symfony/http-kernel": ">=2.3,<2.3.24|~2.4.0|>=2.5,<2.5.9|>=2.6,<2.6.2" - }, - "require-dev": { - "symfony/http-kernel": "~2.8|~3.0|~4.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.4-dev" - } - }, - "autoload": { - "psr-4": { - "Symfony\\Component\\Debug\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony Debug Component", - "homepage": "https://symfony.com", - "time": "2018-02-28T21:49:22+00:00" + "time": "2018-05-30T07:26:09+00:00" }, { "name": "symfony/dom-crawler", - "version": "v3.4.6", + "version": "v4.1.1", "source": { "type": "git", "url": "https://github.com/symfony/dom-crawler.git", - "reference": "2bb5d3101cc01f4fe580e536daf4f1959bc2d24d" + "reference": "3350cacf151b48d903114ab8f7a4ccb23e07e10a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/dom-crawler/zipball/2bb5d3101cc01f4fe580e536daf4f1959bc2d24d", - "reference": "2bb5d3101cc01f4fe580e536daf4f1959bc2d24d", + "url": "https://api.github.com/repos/symfony/dom-crawler/zipball/3350cacf151b48d903114ab8f7a4ccb23e07e10a", + "reference": "3350cacf151b48d903114ab8f7a4ccb23e07e10a", "shasum": "" }, "require": { - "php": "^5.5.9|>=7.0.8", + "php": "^7.1.3", + "symfony/polyfill-ctype": "~1.8", "symfony/polyfill-mbstring": "~1.0" }, "require-dev": { - "symfony/css-selector": "~2.8|~3.0|~4.0" + "symfony/css-selector": "~3.4|~4.0" }, "suggest": { "symfony/css-selector": "" @@ -3738,7 +3727,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "3.4-dev" + "dev-master": "4.1-dev" } }, "autoload": { @@ -3765,34 +3754,34 @@ ], "description": "Symfony DomCrawler Component", "homepage": "https://symfony.com", - "time": "2018-02-22T10:48:49+00:00" + "time": "2018-05-01T23:02:13+00:00" }, { "name": "symfony/event-dispatcher", - "version": "v3.4.6", + "version": "v4.1.1", "source": { "type": "git", "url": "https://github.com/symfony/event-dispatcher.git", - "reference": "58990682ac3fdc1f563b7e705452921372aad11d" + "reference": "2391ed210a239868e7256eb6921b1bd83f3087b5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/58990682ac3fdc1f563b7e705452921372aad11d", - "reference": "58990682ac3fdc1f563b7e705452921372aad11d", + "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/2391ed210a239868e7256eb6921b1bd83f3087b5", + "reference": "2391ed210a239868e7256eb6921b1bd83f3087b5", "shasum": "" }, "require": { - "php": "^5.5.9|>=7.0.8" + "php": "^7.1.3" }, "conflict": { - "symfony/dependency-injection": "<3.3" + "symfony/dependency-injection": "<3.4" }, "require-dev": { "psr/log": "~1.0", - "symfony/config": "~2.8|~3.0|~4.0", - "symfony/dependency-injection": "~3.3|~4.0", - "symfony/expression-language": "~2.8|~3.0|~4.0", - "symfony/stopwatch": "~2.8|~3.0|~4.0" + "symfony/config": "~3.4|~4.0", + "symfony/dependency-injection": "~3.4|~4.0", + "symfony/expression-language": "~3.4|~4.0", + "symfony/stopwatch": "~3.4|~4.0" }, "suggest": { "symfony/dependency-injection": "", @@ -3801,7 +3790,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "3.4-dev" + "dev-master": "4.1-dev" } }, "autoload": { @@ -3828,7 +3817,7 @@ ], "description": "Symfony EventDispatcher Component", "homepage": "https://symfony.com", - "time": "2018-02-14T10:03:57+00:00" + "time": "2018-04-06T07:35:57+00:00" }, { "name": "symfony/filesystem", @@ -3881,25 +3870,25 @@ }, { "name": "symfony/finder", - "version": "v3.4.6", + "version": "v4.1.1", "source": { "type": "git", "url": "https://github.com/symfony/finder.git", - "reference": "a479817ce0a9e4adfd7d39c6407c95d97c254625" + "reference": "84714b8417d19e4ba02ea78a41a975b3efaafddb" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/finder/zipball/a479817ce0a9e4adfd7d39c6407c95d97c254625", - "reference": "a479817ce0a9e4adfd7d39c6407c95d97c254625", + "url": "https://api.github.com/repos/symfony/finder/zipball/84714b8417d19e4ba02ea78a41a975b3efaafddb", + "reference": "84714b8417d19e4ba02ea78a41a975b3efaafddb", "shasum": "" }, "require": { - "php": "^5.5.9|>=7.0.8" + "php": "^7.1.3" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "3.4-dev" + "dev-master": "4.1-dev" } }, "autoload": { @@ -3926,7 +3915,7 @@ ], "description": "Symfony Finder Component", "homepage": "https://symfony.com", - "time": "2018-03-05T18:28:11+00:00" + "time": "2018-06-19T21:38:16+00:00" }, { "name": "symfony/http-foundation", @@ -3982,18 +3971,73 @@ "homepage": "https://symfony.com", "time": "2018-02-22T10:48:49+00:00" }, + { + "name": "symfony/polyfill-ctype", + "version": "v1.8.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-ctype.git", + "reference": "7cc359f1b7b80fc25ed7796be7d96adc9b354bae" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/7cc359f1b7b80fc25ed7796be7d96adc9b354bae", + "reference": "7cc359f1b7b80fc25ed7796be7d96adc9b354bae", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.8-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Polyfill\\Ctype\\": "" + }, + "files": [ + "bootstrap.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + }, + { + "name": "Gert de Pagter", + "email": "BackEndTea@gmail.com" + } + ], + "description": "Symfony polyfill for ctype functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "ctype", + "polyfill", + "portable" + ], + "time": "2018-04-30T19:57:29+00:00" + }, { "name": "symfony/polyfill-mbstring", - "version": "v1.7.0", + "version": "v1.8.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-mbstring.git", - "reference": "78be803ce01e55d3491c1397cf1c64beb9c1b63b" + "reference": "3296adf6a6454a050679cde90f95350ad604b171" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/78be803ce01e55d3491c1397cf1c64beb9c1b63b", - "reference": "78be803ce01e55d3491c1397cf1c64beb9c1b63b", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/3296adf6a6454a050679cde90f95350ad604b171", + "reference": "3296adf6a6454a050679cde90f95350ad604b171", "shasum": "" }, "require": { @@ -4005,7 +4049,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "1.7-dev" + "dev-master": "1.8-dev" } }, "autoload": { @@ -4039,7 +4083,7 @@ "portable", "shim" ], - "time": "2018-01-30T19:27:44+00:00" + "time": "2018-04-26T10:06:28+00:00" }, { "name": "symfony/polyfill-php70", @@ -4151,20 +4195,21 @@ }, { "name": "symfony/yaml", - "version": "v3.4.6", + "version": "v3.4.12", "source": { "type": "git", "url": "https://github.com/symfony/yaml.git", - "reference": "6af42631dcf89e9c616242c900d6c52bd53bd1bb" + "reference": "c5010cc1692ce1fa328b1fb666961eb3d4a85bb0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/yaml/zipball/6af42631dcf89e9c616242c900d6c52bd53bd1bb", - "reference": "6af42631dcf89e9c616242c900d6c52bd53bd1bb", + "url": "https://api.github.com/repos/symfony/yaml/zipball/c5010cc1692ce1fa328b1fb666961eb3d4a85bb0", + "reference": "c5010cc1692ce1fa328b1fb666961eb3d4a85bb0", "shasum": "" }, "require": { - "php": "^5.5.9|>=7.0.8" + "php": "^5.5.9|>=7.0.8", + "symfony/polyfill-ctype": "~1.8" }, "conflict": { "symfony/console": "<3.4" @@ -4205,7 +4250,7 @@ ], "description": "Symfony Yaml Component", "homepage": "https://symfony.com", - "time": "2018-02-16T09:50:28+00:00" + "time": "2018-05-03T23:18:14+00:00" }, { "name": "theseer/tokenizer", @@ -5177,21 +5222,21 @@ }, { "name": "sebastian/phpcpd", - "version": "3.0.1", + "version": "4.0.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpcpd.git", - "reference": "dfed51c1288790fc957c9433e2f49ab152e8a564" + "reference": "bb7953b81fb28e55964d76d5fe2dbe725d43fab3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpcpd/zipball/dfed51c1288790fc957c9433e2f49ab152e8a564", - "reference": "dfed51c1288790fc957c9433e2f49ab152e8a564", + "url": "https://api.github.com/repos/sebastianbergmann/phpcpd/zipball/bb7953b81fb28e55964d76d5fe2dbe725d43fab3", + "reference": "bb7953b81fb28e55964d76d5fe2dbe725d43fab3", "shasum": "" }, "require": { - "php": "^5.6|^7.0", - "phpunit/php-timer": "^1.0.6", + "php": "^7.1", + "phpunit/php-timer": "^2.0", "sebastian/finder-facade": "^1.1", "sebastian/version": "^1.0|^2.0", "symfony/console": "^2.7|^3.0|^4.0" @@ -5202,7 +5247,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "3.0-dev" + "dev-master": "4.0-dev" } }, "autoload": { @@ -5223,7 +5268,7 @@ ], "description": "Copy/Paste Detector (CPD) for PHP code.", "homepage": "https://github.com/sebastianbergmann/phpcpd", - "time": "2017-11-16T08:49:28+00:00" + "time": "2018-02-02T09:59:58+00:00" }, { "name": "squizlabs/php_codesniffer", From 95aa636421933fe23338acb98089408ce7637a63 Mon Sep 17 00:00:00 2001 From: Kevin Kozan <kkozan@magento.com> Date: Tue, 26 Jun 2018 10:46:49 -0500 Subject: [PATCH 077/111] MQE-1079: declare(strict_types=1) causes static code check failure - Updated PHPCodeSniffer version + sniffs. - Removed various @ignore flags in files and fixed errors --- bin/blacklist.txt | 5 +- composer.json | 2 +- composer.lock | 56 +- .../Magento/Sniffs/Annotations/Helper.php | 576 --------------- .../RequireAnnotatedAttributesSniff.php | 357 --------- .../RequireAnnotatedMethodsSniff.php | 694 ------------------ .../Sniffs/Arrays/ShortArraySyntaxSniff.php | 14 +- .../Commenting/FunctionCommentSniff.php | 663 +++++++++++++++++ .../Commenting/VariableCommentSniff.php | 153 ++++ .../Magento/Sniffs/Files/LineLengthSniff.php | 6 +- .../LiteralNamespacesSniff.php | 19 +- .../Sniffs/MicroOptimizations/IsNullSniff.php | 14 +- .../NamingConventions/InterfaceNameSniff.php | 14 +- .../NamingConventions/ReservedWordsSniff.php | 16 +- .../Whitespace/EmptyLineMissedSniff.php | 12 +- .../Whitespace/MultipleEmptyLinesSniff.php | 8 +- dev/tests/static/Magento/ruleset.xml | 9 + .../OperationDefinitionObjectHandlerTest.php | 3 +- .../Objects/EntityDataObjectTest.php | 6 +- .../Test/Objects/ActionObjectTest.php | 5 +- .../Test/Util/ActionMergeUtilTest.php | 2 +- .../Test/Util/ActionObjectExtractorTest.php | 2 +- .../DuplicateNodeValidationUtilTest.php | 1 - dev/tests/unit/Util/MagentoTestCase.php | 1 + .../unit/Util/OperationDefinitionBuilder.php | 2 +- .../unit/Util/OperationElementBuilder.php | 7 +- dev/tests/unit/Util/TestDataArrayBuilder.php | 2 +- dev/tests/unit/Util/TestLoggingUtil.php | 1 + .../Allure/Adapter/MagentoAllureAdapter.php | 2 +- .../Config/Converter.php | 14 +- .../Config/Converter/Dom/Flat.php | 2 +- .../FunctionalTestingFramework/Config/Dom.php | 32 +- .../Config/Dom/ArrayNodeConfig.php | 6 +- .../Config/Dom/NodeMergingConfig.php | 2 +- .../Config/Dom/NodePathMatcher.php | 6 +- .../Config/MftfApplicationConfig.php | 28 +- .../Config/MftfDom.php | 16 +- .../Config/Reader.php | 16 +- .../Config/Reader/Filesystem.php | 16 +- .../Config/Reader/MftfFilesystem.php | 6 +- .../Config/ReplacerInterface.php | 2 +- .../Config/SchemaLocator.php | 2 +- .../Console/BuildProjectCommand.php | 14 +- .../Console/CleanProjectCommand.php | 19 +- .../Console/GenerateSuiteCommand.php | 5 +- .../Console/GenerateTestsCommand.php | 49 +- .../Console/RunTestCommand.php | 12 +- .../Console/RunTestGroupCommand.php | 13 +- .../Console/SetupEnvCommand.php | 3 +- .../Data/Argument/Interpreter/ArrayType.php | 10 +- .../Data/Argument/Interpreter/Boolean.php | 2 +- .../Data/Argument/Interpreter/Composite.php | 5 +- .../Data/Argument/Interpreter/Number.php | 2 +- .../Data/Argument/InterpreterInterface.php | 1 - .../Argument/InterpreterInterface/Proxy.php | 7 +- .../DataGenerator/Config/Dom.php | 14 +- .../DataGenerator/Config/OperationDom.php | 16 +- .../Config/Reader/Filesystem.php | 81 ++ .../Handlers/DataObjectHandler.php | 6 +- .../Objects/EntityDataObject.php | 14 +- .../Objects/OperationDefinitionObject.php | 30 +- .../Objects/OperationElement.php | 14 +- .../Persist/Curl/AdminExecutor.php | 12 +- .../Persist/Curl/FrontendExecutor.php | 8 +- .../Persist/Curl/WebapiExecutor.php | 8 +- .../DataGenerator/Persist/CurlHandler.php | 10 +- .../Persist/DataPersistenceHandler.php | 16 +- .../Persist/OperationDataArrayResolver.php | 22 +- .../Util/OperationElementExtractor.php | 4 +- .../Exceptions/TestFrameworkException.php | 2 +- .../Exceptions/TestReferenceException.php | 2 +- .../Exceptions/XmlException.php | 2 +- .../Extension/ErrorLogger.php | 6 +- .../Extension/TestContextExtension.php | 4 +- .../Helper/EntityRESTApiHelper.php | 4 +- .../Module/MagentoAssert.php | 2 +- .../Module/MagentoRestDriver.php | 78 +- .../Module/MagentoSequence.php | 5 +- .../Module/MagentoWebDriver.php | 84 ++- .../ObjectManager.php | 8 +- .../ObjectManager/Config.php | 2 +- .../ObjectManager/Config/Config.php | 4 +- .../Config/Reader/DomFactory.php | 2 +- .../ObjectManager/ConfigInterface.php | 2 +- .../ObjectManager/Factory.php | 16 +- .../Factory/Dynamic/Developer.php | 14 +- .../ObjectManager/FactoryInterface.php | 2 +- .../ObjectManager/ObjectManager.php | 8 +- .../ObjectManager/Relations/Runtime.php | 2 +- .../ObjectManager/RelationsInterface.php | 2 +- .../ObjectManagerInterface.php | 2 +- .../Page/Config/Dom.php | 14 +- .../Page/Config/SectionDom.php | 14 +- .../Page/Handlers/SectionObjectHandler.php | 2 +- .../Page/Objects/ElementObject.php | 17 +- .../Page/Objects/PageObject.php | 16 +- .../Page/Objects/SectionObject.php | 4 +- .../Suite/Generators/GroupClassGenerator.php | 6 +- .../Suite/Objects/SuiteObject.php | 8 +- .../Suite/SuiteGenerator.php | 12 +- .../Suite/Util/SuiteObjectExtractor.php | 2 +- .../Test/Config/ActionGroupDom.php | 2 +- .../Test/Config/Converter/Dom/Flat.php | 2 +- .../Test/Config/Dom.php | 20 +- .../Test/Objects/ActionGroupObject.php | 44 +- .../Test/Objects/ActionObject.php | 35 +- .../Test/Objects/TestHookObject.php | 5 +- .../Test/Objects/TestObject.php | 18 +- .../Test/Util/ActionMergeUtil.php | 4 +- .../Test/Util/ActionObjectExtractor.php | 6 +- .../Test/Util/AnnotationExtractor.php | 5 +- .../Test/Util/BaseObjectExtractor.php | 4 +- .../Test/Util/TestHookObjectExtractor.php | 2 +- .../Util/ConfigSanitizerUtil.php | 2 +- .../Util/Env/EnvProcessor.php | 8 +- .../Util/Iterator/AbstractIterator.php | 4 +- .../Util/Manifest/BaseTestManifest.php | 2 +- .../Util/Manifest/DefaultTestManifest.php | 2 +- .../Util/Manifest/ParallelTestManifest.php | 10 +- .../Util/Manifest/SingleRunTestManifest.php | 2 +- .../Util/Manifest/TestManifestFactory.php | 7 +- .../FormData/MetadataGenUtil.php | 2 +- .../Swagger/MetadataGenerator.php | 18 +- .../Util/ModuleResolver.php | 2 +- .../Util/Protocol/CurlInterface.php | 10 +- .../Util/Protocol/CurlTransport.php | 16 +- .../Util/Sorter/ParallelGroupSorter.php | 19 +- .../Util/TestGenerator.php | 46 +- .../DuplicateNodeValidationUtil.php | 4 +- .../XmlParser/PageParser.php | 2 +- .../XmlParser/SectionParser.php | 2 +- 131 files changed, 1586 insertions(+), 2264 deletions(-) delete mode 100644 dev/tests/static/Magento/Sniffs/Annotations/Helper.php delete mode 100644 dev/tests/static/Magento/Sniffs/Annotations/RequireAnnotatedAttributesSniff.php delete mode 100644 dev/tests/static/Magento/Sniffs/Annotations/RequireAnnotatedMethodsSniff.php create mode 100644 dev/tests/static/Magento/Sniffs/Commenting/FunctionCommentSniff.php create mode 100644 dev/tests/static/Magento/Sniffs/Commenting/VariableCommentSniff.php create mode 100644 src/Magento/FunctionalTestingFramework/DataGenerator/Config/Reader/Filesystem.php diff --git a/bin/blacklist.txt b/bin/blacklist.txt index ea81ed858..39b3a6700 100644 --- a/bin/blacklist.txt +++ b/bin/blacklist.txt @@ -4,7 +4,6 @@ # THIS FILE CANNOT CONTAIN BLANK LINES # ################################################################### bin/blacklist.txt -dev/tests/static/Magento/Sniffs/Annotations/Helper.php -dev/tests/static/Magento/Sniffs/Annotations/RequireAnnotatedAttributesSniff.php -dev/tests/static/Magento/Sniffs/Annotations/RequireAnnotatedMethodsSniff.php +dev/tests/static/Magento/Sniffs/Commenting/FunctionCommentSniff.php +dev/tests/static/Magento/Sniffs/Commenting/VariableCommentSniff.php dev/tests/verification/_generated diff --git a/composer.json b/composer.json index 8099cd58e..f1164e3df 100755 --- a/composer.json +++ b/composer.json @@ -22,7 +22,7 @@ "vlucas/phpdotenv": "^2.4" }, "require-dev": { - "squizlabs/php_codesniffer": "1.5.3", + "squizlabs/php_codesniffer": "~3.2", "sebastian/phpcpd": "~3.0", "brainmaestro/composer-git-hooks": "^2.3", "doctrine/cache": "<1.7.0", diff --git a/composer.lock b/composer.lock index fd17255c6..4e874012f 100644 --- a/composer.lock +++ b/composer.lock @@ -1,10 +1,10 @@ { "_readme": [ "This file locks the dependencies of your project to a known state", - "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", + "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", "This file is @generated automatically" ], - "content-hash": "723c7b1ff62d50ff5e7d9aeaeb91b77c", + "content-hash": "3075b48da74e90e16b3fe379e679b590", "packages": [ { "name": "allure-framework/allure-codeception", @@ -5227,61 +5227,37 @@ }, { "name": "squizlabs/php_codesniffer", - "version": "1.5.3", + "version": "3.3.0", "source": { "type": "git", "url": "https://github.com/squizlabs/PHP_CodeSniffer.git", - "reference": "396178ada8499ec492363587f037125bf7b07fcc" + "reference": "d86873af43b4aa9d1f39a3601cc0cfcf02b25266" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/squizlabs/PHP_CodeSniffer/zipball/396178ada8499ec492363587f037125bf7b07fcc", - "reference": "396178ada8499ec492363587f037125bf7b07fcc", + "url": "https://api.github.com/repos/squizlabs/PHP_CodeSniffer/zipball/d86873af43b4aa9d1f39a3601cc0cfcf02b25266", + "reference": "d86873af43b4aa9d1f39a3601cc0cfcf02b25266", "shasum": "" }, "require": { + "ext-simplexml": "*", "ext-tokenizer": "*", - "php": ">=5.1.2" + "ext-xmlwriter": "*", + "php": ">=5.4.0" }, - "suggest": { - "phpunit/php-timer": "dev-master" + "require-dev": { + "phpunit/phpunit": "^4.0 || ^5.0 || ^6.0 || ^7.0" }, "bin": [ - "scripts/phpcs" + "bin/phpcs", + "bin/phpcbf" ], "type": "library", "extra": { "branch-alias": { - "dev-phpcs-fixer": "2.0.x-dev" + "dev-master": "3.x-dev" } }, - "autoload": { - "classmap": [ - "CodeSniffer.php", - "CodeSniffer/CLI.php", - "CodeSniffer/Exception.php", - "CodeSniffer/File.php", - "CodeSniffer/Report.php", - "CodeSniffer/Reporting.php", - "CodeSniffer/Sniff.php", - "CodeSniffer/Tokens.php", - "CodeSniffer/Reports/", - "CodeSniffer/CommentParser/", - "CodeSniffer/Tokenizers/", - "CodeSniffer/DocGenerators/", - "CodeSniffer/Standards/AbstractPatternSniff.php", - "CodeSniffer/Standards/AbstractScopeSniff.php", - "CodeSniffer/Standards/AbstractVariableSniff.php", - "CodeSniffer/Standards/IncorrectPatternException.php", - "CodeSniffer/Standards/Generic/Sniffs/", - "CodeSniffer/Standards/MySource/Sniffs/", - "CodeSniffer/Standards/PEAR/Sniffs/", - "CodeSniffer/Standards/PSR1/Sniffs/", - "CodeSniffer/Standards/PSR2/Sniffs/", - "CodeSniffer/Standards/Squiz/Sniffs/", - "CodeSniffer/Standards/Zend/Sniffs/" - ] - }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" @@ -5292,13 +5268,13 @@ "role": "lead" } ], - "description": "PHP_CodeSniffer tokenises PHP, JavaScript and CSS files and detects violations of a defined set of coding standards.", + "description": "PHP_CodeSniffer tokenizes PHP, JavaScript and CSS files and detects violations of a defined set of coding standards.", "homepage": "http://www.squizlabs.com/php-codesniffer", "keywords": [ "phpcs", "standards" ], - "time": "2014-05-01T03:07:07+00:00" + "time": "2018-06-06T23:58:19+00:00" }, { "name": "symfony/config", diff --git a/dev/tests/static/Magento/Sniffs/Annotations/Helper.php b/dev/tests/static/Magento/Sniffs/Annotations/Helper.php deleted file mode 100644 index 53a03b574..000000000 --- a/dev/tests/static/Magento/Sniffs/Annotations/Helper.php +++ /dev/null @@ -1,576 +0,0 @@ -<?php -namespace Magento\Sniffs\Annotations; - -use PHP_CodeSniffer; -use PHP_CodeSniffer_File; - -/** - * Base of the annotations sniffs - * - * @author Greg Sherwood <gsherwood@squiz.net> - * @author Marc McIntyre <mmcintyre@squiz.net> - * @copyright 2006-2012 Squiz Pty Ltd (ABN 77 084 670 600) - * @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence - * @link http://pear.php.net/package/PHP_CodeSniffer - * - * @SuppressWarnings(PHPMD) - */ -class Helper -{ - const ERROR_PARSING = 'ErrorParsing'; - - const AMBIGUOUS_TYPE = 'AmbiguousType'; - - const MISSING = 'Missing'; - - const WRONG_STYLE = 'WrongStyle'; - - const WRONG_END = 'WrongEnd'; - - const FAILED_PARSE = 'FailedParse'; - - const CONTENT_AFTER_OPEN = 'ContentAfterOpen'; - - const MISSING_SHORT = 'MissingShort'; - - const EMPTY_DOC = 'Empty'; - - const SPACING_BETWEEN = 'SpacingBetween'; - - const SPACING_BEFORE_SHORT = 'SpacingBeforeShort'; - - const SPACING_BEFORE_TAGS = 'SpacingBeforeTags'; - - const SHORT_SINGLE_LINE = 'ShortSingleLine'; - - const SHORT_NOT_CAPITAL = 'ShortNotCapital'; - - const SHORT_FULL_STOP = 'ShortFullStop'; - - const SPACING_AFTER = 'SpacingAfter'; - - const SEE_ORDER = 'SeeOrder'; - - const EMPTY_SEE = 'EmptySee'; - - const SEE_INDENT = 'SeeIndent'; - - const DUPLICATE_RETURN = 'DuplicateReturn'; - - const MISSING_PARAM_TAG = 'MissingParamTag'; - - const SPACING_AFTER_LONG_NAME = 'SpacingAfterLongName'; - - const SPACING_AFTER_LONG_TYPE = 'SpacingAfterLongType'; - - const MISSING_PARAM_TYPE = 'MissingParamType'; - - const MISSING_PARAM_NAME = 'MissingParamName'; - - const EXTRA_PARAM_COMMENT = 'ExtraParamComment'; - - const PARAM_NAME_NO_MATCH = 'ParamNameNoMatch'; - - const PARAM_NAME_NO_CASE_MATCH = 'ParamNameNoCaseMatch'; - - const INVALID_TYPE_HINT = 'InvalidTypeHint'; - - const INCORRECT_TYPE_HINT = 'IncorrectTypeHint'; - - const TYPE_HINT_MISSING = 'TypeHintMissing'; - - const INCORRECT_PARAM_VAR_NAME = 'IncorrectParamVarName'; - - const RETURN_ORDER = 'ReturnOrder'; - - const MISSING_RETURN_TYPE = 'MissingReturnType'; - - const INVALID_RETURN = 'InvalidReturn'; - - const INVALID_RETURN_VOID = 'InvalidReturnVoid'; - - const INVALID_NO_RETURN = 'InvalidNoReturn'; - - const INVALID_RETURN_NOT_VOID = 'InvalidReturnNotVoid'; - - const INCORRECT_INHERIT_DOC = 'IncorrectInheritDoc'; - - const RETURN_INDENT = 'ReturnIndent'; - - const MISSING_RETURN = 'MissingReturn'; - - const RETURN_NOT_REQUIRED = 'ReturnNotRequired'; - - const INVALID_THROWS = 'InvalidThrows'; - - const THROWS_NOT_CAPITAL = 'ThrowsNotCapital'; - - const THROWS_ORDER = 'ThrowsOrder'; - - const EMPTY_THROWS = 'EmptyThrows'; - - const THROWS_NO_FULL_STOP = 'ThrowsNoFullStop'; - - const SPACING_AFTER_PARAMS = 'SpacingAfterParams'; - - const SPACING_BEFORE_PARAMS = 'SpacingBeforeParams'; - - const SPACING_BEFORE_PARAM_TYPE = 'SpacingBeforeParamType'; - - const LONG_NOT_CAPITAL = 'LongNotCapital'; - - const TAG_NOT_ALLOWED = 'TagNotAllowed'; - - const DUPLICATE_VAR = 'DuplicateVar'; - - const VAR_ORDER = 'VarOrder'; - - const MISSING_VAR_TYPE = 'MissingVarType'; - - const INCORRECT_VAR_TYPE = 'IncorrectVarType'; - - const VAR_INDENT = 'VarIndent'; - - const MISSING_VAR = 'MissingVar'; - - const MISSING_PARAM_COMMENT = 'MissingParamComment'; - - const PARAM_COMMENT_NOT_CAPITAL = 'ParamCommentNotCapital'; - - const PARAM_COMMENT_FULL_STOP = 'ParamCommentFullStop'; - - // tells phpcs to use the default level - const ERROR = 0; - - // default level of warnings is 5 - const WARNING = 6; - - const INFO = 2; - - // Lowest possible level. - const OFF = 1; - - const LEVEL = 'level'; - - const MESSAGE = 'message'; - - /** - * Map of Error Type to Error Severity - * - * @var array - */ - protected static $reportingLevel = [ - self::ERROR_PARSING => [self::LEVEL => self::ERROR, self::MESSAGE => '%s'], - self::FAILED_PARSE => [self::LEVEL => self::ERROR, self::MESSAGE => '%s'], - self::AMBIGUOUS_TYPE => [ - self::LEVEL => self::WARNING, - self::MESSAGE => 'Ambiguous type "%s" for %s is NOT recommended', - ], - self::MISSING => [self::LEVEL => self::ERROR, self::MESSAGE => 'Missing %s doc comment'], - self::WRONG_STYLE => [ - self::LEVEL => self::WARNING, - self::MESSAGE => 'You must use "/**" style comments for a %s comment', - ], - self::WRONG_END => [ - self::LEVEL => self::WARNING, - self::MESSAGE => 'You must use "*/" to end a function comment; found "%s"', - ], - self::EMPTY_DOC => [self::LEVEL => self::WARNING, self::MESSAGE => '%s doc comment is empty'], - self::CONTENT_AFTER_OPEN => [ - self::LEVEL => self::WARNING, - self::MESSAGE => 'The open comment tag must be the only content on the line', - ], - self::MISSING_SHORT => [ - self::LEVEL => self::WARNING, - self::MESSAGE => 'Missing short description in %s doc comment', - ], - self::SPACING_BETWEEN => [ - self::LEVEL => self::OFF, - self::MESSAGE => 'There must be exactly one blank line between descriptions in %s comment', - ], - self::SPACING_BEFORE_SHORT => [ - self::LEVEL => self::WARNING, - self::MESSAGE => 'Extra newline(s) found before %s comment short description', - ], - self::SPACING_BEFORE_TAGS => [ - self::LEVEL => self::INFO, - self::MESSAGE => 'There must be exactly one blank line before the tags in %s comment', - ], - self::SHORT_SINGLE_LINE => [ - self::LEVEL => self::OFF, - self::MESSAGE => '%s comment short description must be on a single line', - ], - self::SHORT_NOT_CAPITAL => [ - self::LEVEL => self::WARNING, - self::MESSAGE => '%s comment short description must start with a capital letter', - ], - self::SHORT_FULL_STOP => [ - self::LEVEL => self::OFF, - self::MESSAGE => '%s comment short description must end with a full stop', - ], - self::SPACING_AFTER => [ - self::LEVEL => self::WARNING, - self::MESSAGE => 'Additional blank lines found at end of %s comment', - ], - self::SEE_ORDER => [ - self::LEVEL => self::WARNING, - self::MESSAGE => 'The @see tag is in the wrong order; the tag precedes @return', - ], - self::EMPTY_SEE => [ - self::LEVEL => self::WARNING, - self::MESSAGE => 'Content missing for @see tag in %s comment', - ], - self::SEE_INDENT => [ - self::LEVEL => self::OFF, - self::MESSAGE => '@see tag indented incorrectly; expected 1 spaces but found %s', - ], - self::DUPLICATE_RETURN => [ - self::LEVEL => self::ERROR, - self::MESSAGE => 'Only 1 @return tag is allowed in function comment', - ], - self::MISSING_PARAM_TAG => [self::LEVEL => self::ERROR, self::MESSAGE => 'Doc comment for "%s" missing'], - self::SPACING_AFTER_LONG_NAME => [ - self::LEVEL => self::OFF, - self::MESSAGE => 'Expected 1 space after the longest variable name', - ], - self::SPACING_AFTER_LONG_TYPE => [ - self::LEVEL => self::OFF, - self::MESSAGE => 'Expected 1 space after the longest type', - ], - self::MISSING_PARAM_TYPE => [self::LEVEL => self::ERROR, self::MESSAGE => 'Missing type at position %s'], - self::MISSING_PARAM_NAME => [ - self::LEVEL => self::ERROR, - self::MESSAGE => 'Missing parameter name at position %s', - ], - self::EXTRA_PARAM_COMMENT => [ - self::LEVEL => self::WARNING, - self::MESSAGE => 'Superfluous doc comment at position %s', - ], - self::PARAM_NAME_NO_MATCH => [ - self::LEVEL => self::WARNING, - self::MESSAGE => 'Doc comment for var %s does not match actual variable name %s at position %s', - ], - self::PARAM_NAME_NO_CASE_MATCH => [ - self::LEVEL => self::WARNING, - self::MESSAGE => 'Doc comment for var %s does not match case of actual variable name %s at position %s', - ], - self::INVALID_TYPE_HINT => [ - self::LEVEL => self::WARNING, - self::MESSAGE => 'Unknown type hint "%s" found for %s at position %s', - ], - self::INCORRECT_TYPE_HINT => [ - self::LEVEL => self::WARNING, - self::MESSAGE => 'Expected type hint "%s"; found "%s" for %s at position %s', - ], - self::TYPE_HINT_MISSING => [ - self::LEVEL => self::INFO, - self::MESSAGE => 'Type hint "%s" missing for %s at position %s', - ], - self::INCORRECT_PARAM_VAR_NAME => [ - self::LEVEL => self::WARNING, - self::MESSAGE => 'Expected "%s"; found "%s" for %s at position %s', - ], - self::RETURN_ORDER => [ - self::LEVEL => self::WARNING, - self::MESSAGE => 'The @return tag is in the wrong order; the tag follows @see (if used)', - ], - self::MISSING_RETURN_TYPE => [ - self::LEVEL => self::ERROR, - self::MESSAGE => 'Return type missing for @return tag in function comment', - ], - self::INVALID_RETURN => [ - self::LEVEL => self::ERROR, - self::MESSAGE => 'Function return type "%s" is invalid', - ], - self::INVALID_RETURN_VOID => [ - self::LEVEL => self::WARNING, - self::MESSAGE => 'Function return type is void, but function contains return statement', - ], - self::INVALID_NO_RETURN => [ - self::LEVEL => self::WARNING, - self::MESSAGE => 'Function return type is not void, but function has no return statement', - ], - self::INVALID_RETURN_NOT_VOID => [ - self::LEVEL => self::WARNING, - self::MESSAGE => 'Function return type is not void, but function is returning void here', - ], - self::INCORRECT_INHERIT_DOC => [ - self::LEVEL => self::WARNING, - self::MESSAGE => 'The incorrect inherit doc tag usage. Should be {@inheritdoc}', - ], - self::RETURN_INDENT => [ - self::LEVEL => self::OFF, - self::MESSAGE => '@return tag indented incorrectly; expected 1 space but found %s', - ], - self::MISSING_RETURN => [ - self::LEVEL => self::ERROR, - self::MESSAGE => 'Missing @return tag in function comment', - ], - self::RETURN_NOT_REQUIRED => [ - self::LEVEL => self::WARNING, - self::MESSAGE => '@return tag is not required for constructor and destructor', - ], - self::INVALID_THROWS => [ - self::LEVEL => self::WARNING, - self::MESSAGE => 'Exception type and comment missing for @throws tag in function comment', - ], - self::THROWS_NOT_CAPITAL => [ - self::LEVEL => self::WARNING, - self::MESSAGE => '@throws tag comment must start with a capital letter', - ], - self::THROWS_ORDER => [ - self::LEVEL => self::WARNING, - self::MESSAGE => 'The @throws tag is in the wrong order; the tag follows @return', - ], - self::EMPTY_THROWS => [ - self::LEVEL => self::OFF, - self::MESSAGE => 'Comment missing for @throws tag in function comment', - ], - self::THROWS_NO_FULL_STOP => [ - self::LEVEL => self::OFF, - self::MESSAGE => '@throws tag comment must end with a full stop', - ], - self::SPACING_AFTER_PARAMS => [ - self::LEVEL => self::OFF, - self::MESSAGE => 'Last parameter comment requires a blank newline after it', - ], - self::SPACING_BEFORE_PARAMS => [ - self::LEVEL => self::WARNING, - self::MESSAGE => 'Parameters must appear immediately after the comment', - ], - self::SPACING_BEFORE_PARAM_TYPE => [ - self::LEVEL => self::OFF, - self::MESSAGE => 'Expected 1 space before variable type', - ], - self::LONG_NOT_CAPITAL => [ - self::LEVEL => self::WARNING, - self::MESSAGE => '%s comment long description must start with a capital letter', - ], - self::TAG_NOT_ALLOWED => [ - self::LEVEL => self::WARNING, - self::MESSAGE => '@%s tag is not allowed in variable comment', - ], - self::DUPLICATE_VAR => [ - self::LEVEL => self::ERROR, - self::MESSAGE => 'Only 1 @var tag is allowed in variable comment', - ], - self::VAR_ORDER => [ - self::LEVEL => self::WARNING, - self::MESSAGE => 'The @var tag must be the first tag in a variable comment', - ], - self::MISSING_VAR_TYPE => [ - self::LEVEL => self::ERROR, - self::MESSAGE => 'Var type missing for @var tag in variable comment', - ], - self::INCORRECT_VAR_TYPE => [ - self::LEVEL => self::ERROR, - self::MESSAGE => 'Expected "%s"; found "%s" for @var tag in variable comment', - ], - self::VAR_INDENT => [ - self::LEVEL => self::OFF, - self::MESSAGE => '@var tag indented incorrectly; expected 1 space but found %s', - ], - self::MISSING_VAR => [ - self::LEVEL => self::WARNING, - self::MESSAGE => 'Missing @var tag in variable comment', - ], - self::MISSING_PARAM_COMMENT => [ - self::LEVEL => self::OFF, - self::MESSAGE => 'Missing comment for param "%s" at position %s', - ], - self::PARAM_COMMENT_NOT_CAPITAL => [ - self::LEVEL => self::OFF, - self::MESSAGE => 'Param comment must start with a capital letter', - ], - self::PARAM_COMMENT_FULL_STOP => [ - self::LEVEL => self::OFF, - self::MESSAGE => 'Param comment must end with a full stop', - ], - ]; - - /** - * List of allowed types - * - * @var string[] - */ - protected static $allowedTypes = [ - 'array', - 'boolean', - 'bool', - 'float', - 'integer', - 'int', - 'object', - 'string', - 'resource', - 'callable', - 'true', - 'false', - ]; - - /** - * The current PHP_CodeSniffer_File object we are processing. - * - * @var PHP_CodeSniffer_File - */ - protected $currentFile = null; - - /** - * Constructor for class. - * - * @param PHP_CodeSniffer_File $phpcsFile - */ - public function __construct(PHP_CodeSniffer_File $phpcsFile) - { - $this->currentFile = $phpcsFile; - } - - /** - * Returns the current file object - * - * @return PHP_CodeSniffer_File - */ - public function getCurrentFile() - { - return $this->currentFile; - } - - /** - * Returns the eol character used in the file - * - * @return string - */ - public function getEolChar() - { - return $this->currentFile->eolChar; - } - - /** - * Returns the array of allowed types for magento standard - * - * @return string[] - */ - public function getAllowedTypes() - { - return self::$allowedTypes; - } - - /** - * This method will add the message as an error or warning depending on the configuration - * - * @param int $stackPtr The stack position where the error occurred. - * @param string $code A violation code unique to the sniff message. - * @param string[] $data Replacements for the error message. - * @param int $severity The severity level for this error. A value of 0 - * @return void - */ - public function addMessage($stackPtr, $code, $data = [], $severity = 0) - { - // Does the $code key exist in the report level - if (array_key_exists($code, self::$reportingLevel)) { - $message = self::$reportingLevel[$code][self::MESSAGE]; - $level = self::$reportingLevel[$code][self::LEVEL]; - if ($level === self::WARNING || $level === self::INFO || $level === self::OFF) { - $s = $level; - if ($severity !== 0) { - $s = $severity; - } - $this->currentFile->addWarning($message, $stackPtr, $code, $data, $s); - } else { - $this->currentFile->addError($message, $stackPtr, $code, $data, $severity); - } - } - } - - /** - * Returns if we should filter a particular file - * - * @return bool - */ - public function shouldFilter() - { - $shouldFilter = false; - $filename = $this->getCurrentFile()->getFilename(); - if (preg_match('#(?:/|\\\\)dev(?:/|\\\\)tests(?:/|\\\\)#', $filename)) { - // TODO: Temporarily blacklist anything in dev/tests until a sweep of dev/tests can be made. - // This block of the if should be removed leaving only the phtml condition when dev/tests is swept. - // Skip all dev tests files - $shouldFilter = true; - } elseif (preg_match('#(?:/|\\\\)Test(?:/|\\\\)Unit(?:/|\\\\)#', $filename)) { - $shouldFilter = true; - } elseif (preg_match('/\\.phtml$/', $filename)) { - // Skip all phtml files - $shouldFilter = true; - } - - return $shouldFilter; - } - - /** - * Determine if text is a class name - * - * @param string $class - * @return bool - */ - protected function isClassName($class) - { - $return = false; - if (preg_match('/^\\\\?[A-Z]\\w+(?:\\\\\\w+)*?$/', $class)) { - $return = true; - } - return $return; - } - - /** - * Determine if the text has an ambiguous type - * - * @param string $text - * @param array &$matches Type that was detected as ambiguous is in result. - * @return bool - */ - public function isAmbiguous($text, &$matches = []) - { - return preg_match('/(mixed)/', $text, $matches); - } - - /** - * Take the type and suggest the correct one. - * - * @param string $type - * @return string - */ - public function suggestType($type) - { - $suggestedName = null; - // First check to see if this type is a list of types. If so we break it up and check each - if (preg_match('/^.*?(?:\|.*)+$/', $type)) { - // Return list of all types in this string. - $types = explode('|', $type); - if (is_array($types)) { - // Loop over all types and call this method on each. - $suggestions = []; - foreach ($types as $t) { - $suggestions[] = $this->suggestType($t); - } - // Now that we have suggestions put them back together. - $suggestedName = implode('|', $suggestions); - } else { - $suggestedName = 'Unknown'; - } - } elseif ($this->isClassName($type)) { - // If this looks like a class name. - $suggestedName = $type; - } else { - // Only one type First check if that type is a base one. - $lowerVarType = strtolower($type); - if (in_array($lowerVarType, self::$allowedTypes)) { - $suggestedName = $lowerVarType; - } - // If no name suggested yet then call the phpcs version of this method. - if (empty($suggestedName)) { - $suggestedName = PHP_CodeSniffer::suggestType($type); - } - } - return $suggestedName; - } -} diff --git a/dev/tests/static/Magento/Sniffs/Annotations/RequireAnnotatedAttributesSniff.php b/dev/tests/static/Magento/Sniffs/Annotations/RequireAnnotatedAttributesSniff.php deleted file mode 100644 index 98fc99a0e..000000000 --- a/dev/tests/static/Magento/Sniffs/Annotations/RequireAnnotatedAttributesSniff.php +++ /dev/null @@ -1,357 +0,0 @@ -<?php -namespace Magento\Sniffs\Annotations; - -use PHP_CodeSniffer_CommentParser_ClassCommentParser; -use PHP_CodeSniffer_CommentParser_CommentElement; -use PHP_CodeSniffer_CommentParser_MemberCommentParser; -use PHP_CodeSniffer_CommentParser_ParserException; -use PHP_CodeSniffer_File; -use PHP_CodeSniffer_Standards_AbstractVariableSniff; - -include_once 'Helper.php'; -/** - * Parses and verifies the variable doc comment. - * - * Verifies that : - * <ul> - * <li>A variable doc comment exists.</li> - * <li>Short description ends with a full stop.</li> - * <li>There is a blank line after the short description.</li> - * <li>There is a blank line between the description and the tags.</li> - * <li>Check the order, indentation and content of each tag.</li> - * </ul> - * - * @author Greg Sherwood <gsherwood@squiz.net> - * @author Marc McIntyre <mmcintyre@squiz.net> - * @copyright 2006-2012 Squiz Pty Ltd (ABN 77 084 670 600) - * @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence - * @version Release: @package_version@ - * @link http://pear.php.net/package/PHP_CodeSniffer - * - * @SuppressWarnings(PHPMD) - */ -class RequireAnnotatedAttributesSniff extends PHP_CodeSniffer_Standards_AbstractVariableSniff -{ - /** - * The header comment parser for the current file. - * - * @var PHP_CodeSniffer_CommentParser_ClassCommentParser - */ - protected $commentParser = null; - - /** - * The sniff helper for stuff shared between the annotations sniffs - * - * @var Helper - */ - protected $helper = null; - - /** - * Extract the var comment docblock - * - * @param array $tokens - * @param string $commentToken - * @param int $stackPtr The position of the current token in the stack passed in $tokens. - * @return int|false - */ - protected function extractVarDocBlock($tokens, $commentToken, $stackPtr) - { - $commentEnd = $this->helper->getCurrentFile()->findPrevious($commentToken, $stackPtr - 3); - $break = false; - if ($commentEnd !== false && $tokens[$commentEnd]['code'] === T_COMMENT) { - $this->helper->addMessage($stackPtr, Helper::WRONG_STYLE, ['variable']); - $break = true; - } elseif ($commentEnd === false || $tokens[$commentEnd]['code'] !== T_DOC_COMMENT) { - $this->helper->addMessage($stackPtr, Helper::MISSING, ['variable']); - $break = true; - } else { - // Make sure the comment we have found belongs to us. - $commentFor = $this->helper->getCurrentFile()->findNext( - [T_VARIABLE, T_CLASS, T_INTERFACE], - $commentEnd + 1 - ); - if ($commentFor !== $stackPtr) { - $this->helper->addMessage($stackPtr, Helper::MISSING, ['variable']); - $break = true; - } - } - return $break ? false : $commentEnd; - } - - /** - * Checks for short and long descriptions on variable definitions - * - * @param PHP_CodeSniffer_CommentParser_CommentElement $comment - * @param int $commentStart - * @return void - */ - protected function checkForDescription($comment, $commentStart) - { - $short = $comment->getShortComment(); - $long = ''; - $newlineCount = 0; - if (trim($short) === '') { - $this->helper->addMessage($commentStart, Helper::MISSING_SHORT, ['variable']); - $newlineCount = 1; - } else { - // No extra newline before short description. - $newlineSpan = strspn($short, $this->helper->getEolChar()); - if ($short !== '' && $newlineSpan > 0) { - $this->helper->addMessage($commentStart + 1, Helper::SPACING_BEFORE_SHORT, ['variable']); - } - - $newlineCount = substr_count($short, $this->helper->getEolChar()) + 1; - - // Exactly one blank line between short and long description. - $long = $comment->getLongComment(); - if (empty($long) === false) { - $between = $comment->getWhiteSpaceBetween(); - $newlineBetween = substr_count($between, $this->helper->getEolChar()); - if ($newlineBetween !== 2) { - $this->helper->addMessage( - $commentStart + $newlineCount + 1, - Helper::SPACING_BETWEEN, - ['variable'] - ); - } - - $newlineCount += $newlineBetween; - - $testLong = trim($long); - if (preg_match('|\p{Lu}|u', $testLong[0]) === 0) { - $this->helper->addMessage( - $commentStart + $newlineCount, - Helper::LONG_NOT_CAPITAL, - ['Variable'] - ); - } - } - - // Short description must be single line and end with a full stop. - $testShort = trim($short); - $lastChar = $testShort[strlen($testShort) - 1]; - if (substr_count($testShort, $this->helper->getEolChar()) !== 0) { - $this->helper->addMessage($commentStart + 1, Helper::SHORT_SINGLE_LINE, ['Variable']); - } - - if (preg_match('|\p{Lu}|u', $testShort[0]) === 0) { - $this->helper->addMessage($commentStart + 1, Helper::SHORT_NOT_CAPITAL, ['Variable']); - } - - if ($lastChar !== '.') { - $this->helper->addMessage($commentStart + 1, Helper::SHORT_FULL_STOP, ['Variable']); - } - } - // Exactly one blank line before tags. - $tags = $this->commentParser->getTagOrders(); - if (count($tags) > 1) { - $newlineSpan = $comment->getNewlineAfter(); - if ($newlineSpan !== 2) { - if ($long !== '') { - $newlineCount += substr_count($long, $this->helper->getEolChar()) - $newlineSpan + 1; - } - - $this->helper->addMessage( - $commentStart + $newlineCount, - Helper::SPACING_BEFORE_TAGS, - ['variable'] - ); - $short = rtrim($short, $this->helper->getEolChar() . ' '); - } - } - } - - /** - * Called to process class member vars. - * - * @param PHP_CodeSniffer_File $phpcsFile The file being scanned. - * @param int $stackPtr The position of the current token - * in the stack passed in $tokens. - * - * @return void - */ - public function processMemberVar(PHP_CodeSniffer_File $phpcsFile, $stackPtr) - { - $this->helper = new Helper($phpcsFile); - // if we should skip this type we should do that - if ($this->helper->shouldFilter()) { - return; - } - $tokens = $phpcsFile->getTokens(); - $commentToken = [T_COMMENT, T_DOC_COMMENT]; - - // Extract the var comment docblock. - $commentEnd = $this->extractVarDocBlock($tokens, $commentToken, $stackPtr); - if ($commentEnd === false) { - return; - } - - $commentStart = $phpcsFile->findPrevious(T_DOC_COMMENT, $commentEnd - 1, null, true) + 1; - $commentString = $phpcsFile->getTokensAsString($commentStart, $commentEnd - $commentStart + 1); - - // Parse the header comment docblock. - try { - $this->commentParser = new PHP_CodeSniffer_CommentParser_MemberCommentParser($commentString, $phpcsFile); - $this->commentParser->parse(); - } catch (PHP_CodeSniffer_CommentParser_ParserException $e) { - $line = $e->getLineWithinComment() + $commentStart; - $data = [$e->getMessage()]; - $this->helper->addMessage($line, Helper::ERROR_PARSING, $data); - return; - } - - $comment = $this->commentParser->getComment(); - if (($comment === null) === true) { - $this->helper->addMessage($commentStart, Helper::EMPTY_DOC, ['Variable']); - return; - } - - // The first line of the comment should just be the /** code. - $eolPos = strpos($commentString, $phpcsFile->eolChar); - $firstLine = substr($commentString, 0, $eolPos); - if ($firstLine !== '/**') { - $this->helper->addMessage($commentStart, Helper::CONTENT_AFTER_OPEN); - } - - // Check for a comment description. - $this->checkForDescription($comment, $commentStart); - - // Check for unknown/deprecated tags. - $unknownTags = $this->commentParser->getUnknown(); - foreach ($unknownTags as $errorTag) { - // Unknown tags are not parsed, do not process further. - $data = [$errorTag['tag']]; - $this->helper->addMessage($commentStart + $errorTag['line'], Helper::TAG_NOT_ALLOWED, $data); - } - - // Check each tag. - $this->processVar($commentStart, $commentEnd); - $this->processSees($commentStart); - - // The last content should be a newline and the content before - // that should not be blank. If there is more blank space - // then they have additional blank lines at the end of the comment. - $words = $this->commentParser->getWords(); - $lastPos = count($words) - 1; - if (trim( - $words[$lastPos - 1] - ) !== '' || strpos( - $words[$lastPos - 1], - $this->currentFile->eolChar - ) === false || trim( - $words[$lastPos - 2] - ) === '' - ) { - $this->helper->addMessage($commentEnd, Helper::SPACING_AFTER, ['variable']); - } - } - - /** - * Process the var tag. - * - * @param int $commentStart The position in the stack where the comment started. - * @param int $commentEnd The position in the stack where the comment ended. - * - * @return void - */ - protected function processVar($commentStart, $commentEnd) - { - $var = $this->commentParser->getVar(); - - if ($var !== null) { - $errorPos = $commentStart + $var->getLine(); - $index = array_keys($this->commentParser->getTagOrders(), 'var'); - - if (count($index) > 1) { - $this->helper->addMessage($errorPos, Helper::DUPLICATE_VAR); - return; - } - - if ($index[0] !== 1) { - $this->helper->addMessage($errorPos, Helper::VAR_ORDER); - } - - $content = $var->getContent(); - if (empty($content) === true) { - $this->helper->addMessage($errorPos, Helper::MISSING_VAR_TYPE); - return; - } else { - $suggestedType = $this->helper->suggestType($content); - if ($content !== $suggestedType) { - $data = [$suggestedType, $content]; - $this->helper->addMessage($errorPos, Helper::INCORRECT_VAR_TYPE, $data); - } elseif ($this->helper->isAmbiguous($content, $matches)) { - // Warn about ambiguous types ie array or mixed - $data = [$matches[1], '@var']; - $this->helper->addMessage($errorPos, Helper::AMBIGUOUS_TYPE, $data); - } - } - - $spacing = substr_count($var->getWhitespaceBeforeContent(), ' '); - if ($spacing !== 1) { - $data = [$spacing]; - $this->helper->addMessage($errorPos, Helper::VAR_INDENT, $data); - } - } else { - $this->helper->addMessage($commentEnd, Helper::MISSING_VAR); - } - } - - /** - * Process the see tags. - * - * @param int $commentStart The position in the stack where the comment started. - * - * @return void - */ - protected function processSees($commentStart) - { - $sees = $this->commentParser->getSees(); - if (empty($sees) === false) { - foreach ($sees as $see) { - $errorPos = $commentStart + $see->getLine(); - $content = $see->getContent(); - if (empty($content) === true) { - $this->helper->addMessage($errorPos, Helper::EMPTY_SEE, ['variable']); - continue; - } - - $spacing = substr_count($see->getWhitespaceBeforeContent(), ' '); - if ($spacing !== 1) { - $data = [$spacing]; - $this->helper->addMessage($errorPos, Helper::SEE_INDENT, $data); - } - } - } - } - - /** - * Called to process a normal variable. - * - * Not required for this sniff. - * - * @param PHP_CodeSniffer_File $phpcsFile The PHP_CodeSniffer file where this token was found. - * @param int $stackPtr The position where the double quoted - * string was found. - * - * @return void - */ - protected function processVariable(PHP_CodeSniffer_File $phpcsFile, $stackPtr) - { - } - - /** - * Called to process variables found in double quoted strings. - * - * Not required for this sniff. - * - * @param PHP_CodeSniffer_File $phpcsFile The PHP_CodeSniffer file where this token was found. - * @param int $stackPtr The position where the double quoted - * string was found. - * - * @return void - */ - protected function processVariableInString(PHP_CodeSniffer_File $phpcsFile, $stackPtr) - { - } -} diff --git a/dev/tests/static/Magento/Sniffs/Annotations/RequireAnnotatedMethodsSniff.php b/dev/tests/static/Magento/Sniffs/Annotations/RequireAnnotatedMethodsSniff.php deleted file mode 100644 index 63dc220b6..000000000 --- a/dev/tests/static/Magento/Sniffs/Annotations/RequireAnnotatedMethodsSniff.php +++ /dev/null @@ -1,694 +0,0 @@ -<?php -namespace Magento\Sniffs\Annotations; - -use PHP_CodeSniffer_CommentParser_FunctionCommentParser; -use PHP_CodeSniffer_CommentParser_ParserException; -use PHP_CodeSniffer_File; -use PHP_CodeSniffer_Sniff; -use PHP_CodeSniffer_Tokens; - -include_once 'Helper.php'; -/** - * Parses and verifies the doc comments for functions. - * - * PHP version 5 - * - * @author Greg Sherwood <gsherwood@squiz.net> - * @author Marc McIntyre <mmcintyre@squiz.net> - * @copyright 2006-2012 Squiz Pty Ltd (ABN 77 084 670 600) - * @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence - * @link http://pear.php.net/package/PHP_CodeSniffer - * - * @SuppressWarnings(PHPMD) - */ -class RequireAnnotatedMethodsSniff implements PHP_CodeSniffer_Sniff -{ - /** - * The name of the method that we are currently processing. - * - * @var string - */ - private $_methodName = ''; - - /** - * The position in the stack where the function token was found. - * - * @var int - */ - private $_functionToken = null; - - /** - * The position in the stack where the class token was found. - * - * @var int - */ - private $_classToken = null; - - /** - * The index of the current tag we are processing. - * - * @var int - */ - private $_tagIndex = 0; - - /** - * The function comment parser for the current method. - * - * @var PHP_CodeSniffer_CommentParser_FunctionCommentParser - */ - protected $commentParser = null; - - /** - * The sniff helper for stuff shared between the annotations sniffs - * - * @var Helper - */ - protected $helper = null; - - /** - * Returns an array of tokens this test wants to listen for. - * - * @return array - */ - public function register() - { - return [T_FUNCTION]; - } - - /** - * Processes this test, when one of its tokens is encountered. - * - * @param PHP_CodeSniffer_File $phpcsFile The file being scanned. - * @param int $stackPtr The position of the current token - * in the stack passed in $tokens. - * - * @return void - */ - public function process(PHP_CodeSniffer_File $phpcsFile, $stackPtr) - { - $this->helper = new Helper($phpcsFile); - // if we should skip this type we should do that - if ($this->helper->shouldFilter()) { - return; - } - - $tokens = $phpcsFile->getTokens(); - - $find = [T_COMMENT, T_DOC_COMMENT, T_CLASS, T_FUNCTION, T_OPEN_TAG]; - - $commentEnd = $phpcsFile->findPrevious($find, $stackPtr - 1); - - if ($commentEnd === false) { - return; - } - - // If the token that we found was a class or a function, then this - // function has no doc comment. - $code = $tokens[$commentEnd]['code']; - - if ($code === T_COMMENT) { - // The function might actually be missing a comment, and this last comment - // found is just commenting a bit of code on a line. So if it is not the - // only thing on the line, assume we found nothing. - $prevContent = $phpcsFile->findPrevious(PHP_CodeSniffer_Tokens::$emptyTokens, $commentEnd); - if ($tokens[$commentEnd]['line'] === $tokens[$commentEnd]['line']) { - $this->helper->addMessage($stackPtr, Helper::MISSING, ['function']); - } else { - $this->helper->addMessage($stackPtr, Helper::WRONG_STYLE, ['function']); - } - return; - } elseif ($code !== T_DOC_COMMENT) { - $this->helper->addMessage($stackPtr, Helper::MISSING, ['function']); - return; - } elseif (trim($tokens[$commentEnd]['content']) !== '*/') { - $this->helper->addMessage($commentEnd, Helper::WRONG_END, [trim($tokens[$commentEnd]['content'])]); - return; - } - - // If there is any code between the function keyword and the doc block - // then the doc block is not for us. - $ignore = PHP_CodeSniffer_Tokens::$scopeModifiers; - $ignore[] = T_STATIC; - $ignore[] = T_WHITESPACE; - $ignore[] = T_ABSTRACT; - $ignore[] = T_FINAL; - $prevToken = $phpcsFile->findPrevious($ignore, $stackPtr - 1, null, true); - if ($prevToken !== $commentEnd) { - $this->helper->addMessage($stackPtr, Helper::MISSING, ['function']); - return; - } - - $this->_functionToken = $stackPtr; - - $this->_classToken = null; - foreach ($tokens[$stackPtr]['conditions'] as $condPtr => $condition) { - if ($condition === T_CLASS || $condition === T_INTERFACE) { - $this->_classToken = $condPtr; - break; - } - } - - // Find the first doc comment. - $commentStart = $phpcsFile->findPrevious(T_DOC_COMMENT, $commentEnd - 1, null, true) + 1; - $commentString = $phpcsFile->getTokensAsString($commentStart, $commentEnd - $commentStart + 1); - $this->_methodName = $phpcsFile->getDeclarationName($stackPtr); - - try { - $this->commentParser = new PHP_CodeSniffer_CommentParser_FunctionCommentParser($commentString, $phpcsFile); - $this->commentParser->parse(); - } catch (PHP_CodeSniffer_CommentParser_ParserException $e) { - $line = $e->getLineWithinComment() + $commentStart; - $this->helper->addMessage($line, Helper::FAILED_PARSE, [$e->getMessage()]); - return; - } - - $comment = $this->commentParser->getComment(); - if (($comment === null) === true) { - $this->helper->addMessage($commentStart, Helper::EMPTY_DOC, ['Function']); - return; - } - - // The first line of the comment should just be the /** code. - $eolPos = strpos($commentString, $phpcsFile->eolChar); - $firstLine = substr($commentString, 0, $eolPos); - if ($firstLine !== '/**') { - $this->helper->addMessage($commentStart, Helper::CONTENT_AFTER_OPEN); - } - - // If the comment has an inherit doc note just move on - if (preg_match('/\{\@inheritdoc\}/', $commentString)) { - return; - } elseif (preg_match('/\{?\@?inherit[dD]oc\}?/', $commentString)) { - $this->helper->addMessage($commentStart, Helper::INCORRECT_INHERIT_DOC); - return; - } - - $this->processParams($commentStart, $commentEnd); - $this->processSees($commentStart); - $this->processReturn($commentStart, $commentEnd); - $this->processThrows($commentStart); - - // Check for a comment description. - $short = $comment->getShortComment(); - if (trim($short) === '') { - $this->helper->addMessage($commentStart, Helper::MISSING_SHORT, ['function']); - return; - } - - // No extra newline before short description. - $newlineCount = 0; - $newlineSpan = strspn($short, $phpcsFile->eolChar); - if ($short !== '' && $newlineSpan > 0) { - $this->helper->addMessage($commentStart + 1, Helper::SPACING_BEFORE_SHORT, ['function']); - } - - $newlineCount = substr_count($short, $phpcsFile->eolChar) + 1; - - // Exactly one blank line between short and long description. - $long = $comment->getLongComment(); - if (empty($long) === false) { - $between = $comment->getWhiteSpaceBetween(); - $newlineBetween = substr_count($between, $phpcsFile->eolChar); - if ($newlineBetween !== 2) { - $this->helper->addMessage( - $commentStart + $newlineCount + 1, - Helper::SPACING_BETWEEN, - ['function'] - ); - } - $newlineCount += $newlineBetween; - $testLong = trim($long); - if (preg_match('|\p{Lu}|u', $testLong[0]) === 0) { - $this->helper->addMessage($commentStart + $newlineCount, Helper::LONG_NOT_CAPITAL, ['Function']); - } - } - - // Exactly one blank line before tags. - $params = $this->commentParser->getTagOrders(); - if (count($params) > 1) { - $newlineSpan = $comment->getNewlineAfter(); - if ($newlineSpan !== 2) { - if ($long !== '') { - $newlineCount += substr_count($long, $phpcsFile->eolChar) - $newlineSpan + 1; - } - - $this->helper->addMessage( - $commentStart + $newlineCount, - Helper::SPACING_BEFORE_TAGS, - ['function'] - ); - $short = rtrim($short, $phpcsFile->eolChar . ' '); - } - } - - // Short description must be single line and end with a full stop. - $testShort = trim($short); - $lastChar = $testShort[strlen($testShort) - 1]; - if (substr_count($testShort, $phpcsFile->eolChar) !== 0) { - $this->helper->addMessage($commentStart + 1, Helper::SHORT_SINGLE_LINE, ['Function']); - } - - if (preg_match('|\p{Lu}|u', $testShort[0]) === 0) { - $this->helper->addMessage($commentStart + 1, Helper::SHORT_NOT_CAPITAL, ['Function']); - } - - if ($lastChar !== '.') { - $this->helper->addMessage($commentStart + 1, Helper::SHORT_FULL_STOP, ['Function']); - } - - // Check for unknown/deprecated tags. - // For example call: $this->processUnknownTags($commentStart, $commentEnd); - - // The last content should be a newline and the content before - // that should not be blank. If there is more blank space - // then they have additional blank lines at the end of the comment. - $words = $this->commentParser->getWords(); - $lastPos = count($words) - 1; - if (trim( - $words[$lastPos - 1] - ) !== '' || strpos( - $words[$lastPos - 1], - $this->helper->getCurrentFile()->eolChar - ) === false || trim( - $words[$lastPos - 2] - ) === '' - ) { - $this->helper->addMessage($commentEnd, Helper::SPACING_AFTER, ['function']); - } - } - - /** - * Process the see tags. - * - * @param int $commentStart The position in the stack where the comment started. - * - * @return void - */ - protected function processSees($commentStart) - { - $sees = $this->commentParser->getSees(); - if (empty($sees) === false) { - $tagOrder = $this->commentParser->getTagOrders(); - $index = array_keys($this->commentParser->getTagOrders(), 'see'); - foreach ($sees as $i => $see) { - $errorPos = $commentStart + $see->getLine(); - $since = array_keys($tagOrder, 'since'); - if (count($since) === 1 && $this->_tagIndex !== 0) { - $this->_tagIndex++; - if ($index[$i] !== $this->_tagIndex) { - $this->helper->addMessage($errorPos, Helper::SEE_ORDER); - } - } - - $content = $see->getContent(); - if (empty($content) === true) { - $this->helper->addMessage($errorPos, Helper::EMPTY_SEE, ['function']); - continue; - } - } - } - } - - /** - * Process the return comment of this function comment. - * - * @param int $commentStart The position in the stack where the comment started. - * @param int $commentEnd The position in the stack where the comment ended. - * - * @return void - */ - protected function processReturn($commentStart, $commentEnd) - { - // Skip constructor and destructor. - $className = ''; - if ($this->_classToken !== null) { - $className = $this->helper->getCurrentFile()->getDeclarationName($this->_classToken); - $className = strtolower(ltrim($className, '_')); - } - - $methodName = strtolower(ltrim($this->_methodName, '_')); - $return = $this->commentParser->getReturn(); - - if ($this->_methodName !== '__construct' && $this->_methodName !== '__destruct') { - if ($return !== null) { - $tagOrder = $this->commentParser->getTagOrders(); - $index = array_keys($tagOrder, 'return'); - $errorPos = $commentStart + $return->getLine(); - $content = trim($return->getRawContent()); - - if (count($index) > 1) { - $this->helper->addMessage($errorPos, Helper::DUPLICATE_RETURN); - return; - } - - $since = array_keys($tagOrder, 'since'); - if (count($since) === 1 && $this->_tagIndex !== 0) { - $this->_tagIndex++; - if ($index[0] !== $this->_tagIndex) { - $this->helper->addMessage($errorPos, Helper::RETURN_ORDER); - } - } - - if (empty($content) === true) { - $this->helper->addMessage($errorPos, Helper::MISSING_RETURN_TYPE); - } else { - // Strip off any comments attached to our content - $parts = explode(' ', $content); - $content = $parts[0]; - // Check return type (can be multiple, separated by '|'). - $typeNames = explode('|', $content); - $suggestedNames = []; - foreach ($typeNames as $i => $typeName) { - $suggestedName = $this->helper->suggestType($typeName); - if (in_array($suggestedName, $suggestedNames) === false) { - $suggestedNames[] = $suggestedName; - } - } - - $suggestedType = implode('|', $suggestedNames); - if ($content !== $suggestedType) { - $data = [$content]; - $this->helper->addMessage($errorPos, Helper::INVALID_RETURN, $data); - } elseif ($this->helper->isAmbiguous($typeName, $matches)) { - // Warn about ambiguous types ie array or mixed - $data = [$matches[1], '@return']; - $this->helper->addMessage($errorPos, Helper::AMBIGUOUS_TYPE, $data); - } - - $tokens = $this->helper->getCurrentFile()->getTokens(); - - // If the return type is void, make sure there is - // no return statement in the function. - if ($content === 'void') { - if (isset($tokens[$this->_functionToken]['scope_closer']) === true) { - $endToken = $tokens[$this->_functionToken]['scope_closer']; - - $tokens = $this->helper->getCurrentFile()->getTokens(); - for ($returnToken = $this->_functionToken; $returnToken < $endToken; $returnToken++) { - if ($tokens[$returnToken]['code'] === T_CLOSURE) { - $returnToken = $tokens[$returnToken]['scope_closer']; - continue; - } - - if ($tokens[$returnToken]['code'] === T_RETURN) { - break; - } - } - - if ($returnToken !== $endToken) { - // If the function is not returning anything, just - // exiting, then there is no problem. - $semicolon = $this->helper->getCurrentFile()->findNext( - T_WHITESPACE, - $returnToken + 1, - null, - true - ); - if ($tokens[$semicolon]['code'] !== T_SEMICOLON) { - $this->helper->addMessage($errorPos, Helper::INVALID_RETURN_VOID); - } - } - } - } elseif ($content !== 'mixed') { - // If return type is not void, there needs to be a - // returns statement somewhere in the function that - // returns something. - if (isset($tokens[$this->_functionToken]['scope_closer']) === true) { - $endToken = $tokens[$this->_functionToken]['scope_closer']; - $returnToken = $this->helper->getCurrentFile()->findNext( - T_RETURN, - $this->_functionToken, - $endToken - ); - if ($returnToken === false) { - $this->helper->addMessage($errorPos, Helper::INVALID_NO_RETURN); - } else { - $semicolon = $this->helper->getCurrentFile()->findNext( - T_WHITESPACE, - $returnToken + 1, - null, - true - ); - if ($tokens[$semicolon]['code'] === T_SEMICOLON) { - $this->helper->addMessage($returnToken, Helper::INVALID_RETURN_NOT_VOID); - } - } - } - } - - $spacing = substr_count($return->getWhitespaceBeforeValue(), ' '); - if ($spacing !== 1) { - $data = [$spacing]; - $this->helper->addMessage($errorPos, Helper::RETURN_INDENT, $data); - } - } - } else { - $this->helper->addMessage($commentEnd, Helper::MISSING_RETURN); - } - } elseif ($return !== null) { - // No return tag for constructor and destructor. - $errorPos = $commentStart + $return->getLine(); - $this->helper->addMessage($errorPos, Helper::RETURN_NOT_REQUIRED); - } - } - - /** - * Process any throw tags that this function comment has. - * - * @param int $commentStart The position in the stack where the comment started. - * - * @return void - */ - protected function processThrows($commentStart) - { - if (count($this->commentParser->getThrows()) === 0) { - return; - } - - $tagOrder = $this->commentParser->getTagOrders(); - $index = array_keys($this->commentParser->getTagOrders(), 'throws'); - - foreach ($this->commentParser->getThrows() as $i => $throw) { - $exception = $throw->getValue(); - $content = trim($throw->getComment()); - $errorPos = $commentStart + $throw->getLine(); - if (empty($exception) === true) { - $this->helper->addMessage($errorPos, Helper::INVALID_THROWS); - } elseif (empty($content) === true) { - $this->helper->addMessage($errorPos, Helper::EMPTY_THROWS); - } else { - // Assumes that $content is not empty. - // Starts with a capital letter and ends with a fullstop. - $firstChar = $content[0]; - if (strtoupper($firstChar) !== $firstChar) { - $this->helper->addMessage($errorPos, Helper::THROWS_NOT_CAPITAL); - } - - $lastChar = $content[strlen($content) - 1]; - if ($lastChar !== '.') { - $this->helper->addMessage($errorPos, Helper::THROWS_NO_FULL_STOP); - } - } - - $since = array_keys($tagOrder, 'since'); - if (count($since) === 1 && $this->_tagIndex !== 0) { - $this->_tagIndex++; - if ($index[$i] !== $this->_tagIndex) { - $this->helper->addMessage($errorPos, Helper::THROWS_ORDER); - } - } - } - } - - /** - * Process the function parameter comments. - * - * @param int $commentStart The position in the stack where - * the comment started. - * @param int $commentEnd The position in the stack where - * the comment ended. - * - * @return void - */ - protected function processParams($commentStart, $commentEnd) - { - $realParams = $this->helper->getCurrentFile()->getMethodParameters($this->_functionToken); - $params = $this->commentParser->getParams(); - $foundParams = []; - - if (empty($params) === false) { - $subStrCount = substr_count( - $params[count($params) - 1]->getWhitespaceAfter(), - $this->helper->getCurrentFile()->eolChar - ); - if ($subStrCount !== 2) { - $errorPos = $params[count($params) - 1]->getLine() + $commentStart; - $this->helper->addMessage($errorPos, Helper::SPACING_AFTER_PARAMS); - } - - // Parameters must appear immediately after the comment. - if ($params[0]->getOrder() !== 2) { - $errorPos = $params[0]->getLine() + $commentStart; - $this->helper->addMessage($errorPos, Helper::SPACING_BEFORE_PARAMS); - } - - $previousParam = null; - $spaceBeforeVar = 10000; - $spaceBeforeComment = 10000; - $longestType = 0; - $longestVar = 0; - - foreach ($params as $param) { - $paramComment = trim($param->getComment()); - $errorPos = $param->getLine() + $commentStart; - - // Make sure that there is only one space before the var type. - if ($param->getWhitespaceBeforeType() !== ' ') { - $this->helper->addMessage($errorPos, Helper::SPACING_BEFORE_PARAM_TYPE); - } - - $spaceCount = substr_count($param->getWhitespaceBeforeVarName(), ' '); - if ($spaceCount < $spaceBeforeVar) { - $spaceBeforeVar = $spaceCount; - $longestType = $errorPos; - } - - $spaceCount = substr_count($param->getWhitespaceBeforeComment(), ' '); - - if ($spaceCount < $spaceBeforeComment && $paramComment !== '') { - $spaceBeforeComment = $spaceCount; - $longestVar = $errorPos; - } - - // Make sure they are in the correct order, and have the correct name. - $pos = $param->getPosition(); - $paramName = $param->getVarName() !== '' ? $param->getVarName() : '[ UNKNOWN ]'; - - if ($previousParam !== null) { - $previousName = $previousParam->getVarName() !== '' ? $previousParam->getVarName() : 'UNKNOWN'; - } - - // Variable must be one of the supported standard type. - $typeNames = explode('|', $param->getType()); - foreach ($typeNames as $typeName) { - $suggestedName = $this->helper->suggestType($typeName); - if ($typeName !== $suggestedName) { - $data = [$suggestedName, $typeName, $paramName, $pos]; - $this->helper->addMessage($errorPos, Helper::INCORRECT_PARAM_VAR_NAME, $data); - } elseif ($this->helper->isAmbiguous($typeName, $matches)) { - // Warn about ambiguous types ie array or mixed - $data = [$matches[1], $paramName, ' at position ' . $pos . ' is NOT recommended']; - $this->helper->addMessage($commentEnd + 2, Helper::AMBIGUOUS_TYPE, $data); - } elseif (count($typeNames) === 1) { - // Check type hint for array and custom type. - $suggestedTypeHint = ''; - if (strpos($suggestedName, 'array') !== false) { - $suggestedTypeHint = 'array'; - } elseif (strpos($suggestedName, 'callable') !== false) { - $suggestedTypeHint = 'callable'; - } elseif (in_array($typeName, $this->helper->getAllowedTypes()) === false) { - $suggestedTypeHint = $suggestedName; - } else { - $suggestedTypeHint = $this->helper->suggestType($typeName); - } - - if ($suggestedTypeHint !== '' && isset($realParams[$pos - 1]) === true) { - $typeHint = $realParams[$pos - 1]['type_hint']; - if ($typeHint === '') { - $data = [$suggestedTypeHint, $paramName, $pos]; - $this->helper->addMessage($commentEnd + 2, Helper::TYPE_HINT_MISSING, $data); - } elseif ($typeHint !== $suggestedTypeHint) { - $data = [$suggestedTypeHint, $typeHint, $paramName, $pos]; - $this->helper->addMessage($commentEnd + 2, Helper::INCORRECT_TYPE_HINT, $data); - } - } elseif ($suggestedTypeHint === '' && isset($realParams[$pos - 1]) === true) { - $typeHint = $realParams[$pos - 1]['type_hint']; - if ($typeHint !== '') { - $data = [$typeHint, $paramName, $pos]; - $this->helper->addMessage($commentEnd + 2, Helper::INVALID_TYPE_HINT, $data); - } - } - } - } - - // Make sure the names of the parameter comment matches the - // actual parameter. - if (isset($realParams[$pos - 1]) === true) { - $realName = $realParams[$pos - 1]['name']; - $foundParams[] = $realName; - - // Append ampersand to name if passing by reference. - if ($realParams[$pos - 1]['pass_by_reference'] === true) { - $realName = '&' . $realName; - } - - if ($realName !== $paramName) { - $code = Helper::PARAM_NAME_NO_MATCH; - $data = [$paramName, $realName, $pos]; - - if (strtolower($paramName) === strtolower($realName)) { - $code = Helper::PARAM_NAME_NO_CASE_MATCH; - } - - $this->helper->addMessage($errorPos, $code, $data); - } - } elseif (substr($paramName, -4) !== ',...') { - // We must have an extra parameter comment. - $this->helper->addMessage($errorPos, Helper::EXTRA_PARAM_COMMENT, [$pos]); - } - - if ($param->getVarName() === '') { - $this->helper->addMessage($errorPos, Helper::MISSING_PARAM_NAME, [$pos]); - } - - if ($param->getType() === '') { - $this->helper->addMessage($errorPos, Helper::MISSING_PARAM_TYPE, [$pos]); - } - - if ($paramComment === '') { - $data = [$paramName, $pos]; - $this->helper->addMessage($errorPos, Helper::MISSING_PARAM_COMMENT, $data); - } else { - // Param comments must start with a capital letter and - // end with the full stop. - $firstChar = $paramComment[0]; - if (preg_match('|\p{Lu}|u', $firstChar) === 0) { - $this->helper->addMessage($errorPos, Helper::PARAM_COMMENT_NOT_CAPITAL); - } - $lastChar = $paramComment[strlen($paramComment) - 1]; - if ($lastChar !== '.') { - $this->helper->addMessage($errorPos, Helper::PARAM_COMMENT_FULL_STOP); - } - } - - $previousParam = $param; - } - - if ($spaceBeforeVar !== 1 && $spaceBeforeVar !== 10000 && $spaceBeforeComment !== 10000) { - $this->helper->addMessage($longestType, Helper::SPACING_AFTER_LONG_TYPE); - } - - if ($spaceBeforeComment !== 1 && $spaceBeforeComment !== 10000) { - $this->helper->addMessage($longestVar, Helper::SPACING_AFTER_LONG_NAME); - } - } - - $realNames = []; - foreach ($realParams as $realParam) { - $realNames[] = $realParam['name']; - } - - // Report missing comments. - $diff = array_diff($realNames, $foundParams); - foreach ($diff as $neededParam) { - if (count($params) !== 0) { - $errorPos = $params[count($params) - 1]->getLine() + $commentStart; - } else { - $errorPos = $commentStart; - } - - $data = [$neededParam]; - $this->helper->addMessage($errorPos, Helper::MISSING_PARAM_TAG, $data); - } - } -} diff --git a/dev/tests/static/Magento/Sniffs/Arrays/ShortArraySyntaxSniff.php b/dev/tests/static/Magento/Sniffs/Arrays/ShortArraySyntaxSniff.php index e8e850706..9013f12b6 100644 --- a/dev/tests/static/Magento/Sniffs/Arrays/ShortArraySyntaxSniff.php +++ b/dev/tests/static/Magento/Sniffs/Arrays/ShortArraySyntaxSniff.php @@ -5,10 +5,10 @@ */ namespace Magento\Sniffs\Arrays; -use PHP_CodeSniffer_File; -use PHP_CodeSniffer_Sniff; +use PHP_CodeSniffer\Sniffs\Sniff; +use PHP_CodeSniffer\Files\File; -class ShortArraySyntaxSniff implements PHP_CodeSniffer_Sniff +class ShortArraySyntaxSniff implements Sniff { /** * {@inheritdoc} @@ -21,8 +21,12 @@ public function register() /** * {@inheritdoc} */ - public function process(PHP_CodeSniffer_File $sourceFile, $stackPtr) + public function process(File $sourceFile, $stackPtr) { - $sourceFile->addError('Short array syntax must be used; expected "[]" but found "array()"', $stackPtr); + $sourceFile->addError( + 'Short array syntax must be used; expected "[]" but found "array()"', + $stackPtr, + 'ShortArraySyntax' + ); } } diff --git a/dev/tests/static/Magento/Sniffs/Commenting/FunctionCommentSniff.php b/dev/tests/static/Magento/Sniffs/Commenting/FunctionCommentSniff.php new file mode 100644 index 000000000..2bd8be194 --- /dev/null +++ b/dev/tests/static/Magento/Sniffs/Commenting/FunctionCommentSniff.php @@ -0,0 +1,663 @@ +<?php +/** + * Parses and verifies the doc comments for functions. + * + * @author Greg Sherwood <gsherwood@squiz.net> + * @copyright 2006-2015 Squiz Pty Ltd (ABN 77 084 670 600) + * @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence + */ + +namespace Magento\Sniffs\Commenting; + +use PHP_CodeSniffer\Standards\PEAR\Sniffs\Commenting\FunctionCommentSniff as PEARFunctionCommentSniff; +use PHP_CodeSniffer\Files\File; +use PHP_CodeSniffer\Config; +use PHP_CodeSniffer\Util\Common; + +class FunctionCommentSniff extends PEARFunctionCommentSniff +{ + + /** + * The current PHP version. + * + * @var integer + */ + private $phpVersion = null; + + + /** + * Process the return comment of this function comment. + * + * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned. + * @param int $stackPtr The position of the current token + * in the stack passed in $tokens. + * @param int $commentStart The position in the stack where the comment started. + * + * @return void + */ + protected function processReturn(File $phpcsFile, $stackPtr, $commentStart) + { + $tokens = $phpcsFile->getTokens(); + + // Skip constructor and destructor. + $methodName = $phpcsFile->getDeclarationName($stackPtr); + $isSpecialMethod = ($methodName === '__construct' || $methodName === '__destruct'); + + $return = null; + foreach ($tokens[$commentStart]['comment_tags'] as $tag) { + if ($tokens[$tag]['content'] === '@return') { + if ($return !== null) { + $error = 'Only 1 @return tag is allowed in a function comment'; + $phpcsFile->addError($error, $tag, 'DuplicateReturn'); + return; + } + + $return = $tag; + } + } + + + if ($isSpecialMethod === true) { + return; + } + + if ($return !== null) { + $content = $tokens[($return + 2)]['content']; + if (empty($content) === true || $tokens[($return + 2)]['code'] !== T_DOC_COMMENT_STRING) { + $error = 'Return type missing for @return tag in function comment'; + $phpcsFile->addError($error, $return, 'MissingReturnType'); + } else { + // Support both a return type and a description. + $split = preg_match('`^((?:\|?(?:array\([^\)]*\)|[\\\\a-z0-9\[\]]+))*)( .*)?`i', $content, $returnParts); + if (isset($returnParts[1]) === false) { + return; + } + + $returnType = $returnParts[1]; + + // Check return type (can be multiple, separated by '|'). + $typeNames = explode('|', $returnType); + $suggestedNames = array(); + foreach ($typeNames as $i => $typeName) { + $suggestedName = Common::suggestType($typeName); + if (in_array($suggestedName, $suggestedNames) === false) { + $suggestedNames[] = $suggestedName; + } + } + + $suggestedType = implode('|', $suggestedNames); + if ($returnType !== $suggestedType) { + $error = 'Expected "%s" but found "%s" for function return type'; + $data = array( + $suggestedType, + $returnType, + ); + $fix = $phpcsFile->addFixableError($error, $return, 'InvalidReturn', $data); + if ($fix === true) { + $replacement = $suggestedType; + if (empty($returnParts[2]) === false) { + $replacement .= $returnParts[2]; + } + + $phpcsFile->fixer->replaceToken(($return + 2), $replacement); + unset($replacement); + } + } + + // If the return type is void, make sure there is + // no return statement in the function. + if ($returnType === 'void') { + if (isset($tokens[$stackPtr]['scope_closer']) === true) { + $endToken = $tokens[$stackPtr]['scope_closer']; + for ($returnToken = $stackPtr; $returnToken < $endToken; $returnToken++) { + if ($tokens[$returnToken]['code'] === T_CLOSURE + || $tokens[$returnToken]['code'] === T_ANON_CLASS + ) { + $returnToken = $tokens[$returnToken]['scope_closer']; + continue; + } + + if ($tokens[$returnToken]['code'] === T_RETURN + || $tokens[$returnToken]['code'] === T_YIELD + || $tokens[$returnToken]['code'] === T_YIELD_FROM + ) { + break; + } + } + + if ($returnToken !== $endToken) { + // If the function is not returning anything, just + // exiting, then there is no problem. + $semicolon = $phpcsFile->findNext(T_WHITESPACE, ($returnToken + 1), null, true); + if ($tokens[$semicolon]['code'] !== T_SEMICOLON) { + $error = 'Function return type is void, but function contains return statement'; + $phpcsFile->addError($error, $return, 'InvalidReturnVoid'); + } + } + }//end if + } else if ($returnType !== 'mixed' && in_array('void', $typeNames, true) === false) { + // If return type is not void, there needs to be a return statement + // somewhere in the function that returns something. + if (isset($tokens[$stackPtr]['scope_closer']) === true) { + $endToken = $tokens[$stackPtr]['scope_closer']; + $returnToken = $phpcsFile->findNext(array(T_RETURN, T_YIELD, T_YIELD_FROM), $stackPtr, $endToken); + if ($returnToken === false) { + $error = 'Function return type is not void, but function has no return statement'; + $phpcsFile->addError($error, $return, 'InvalidNoReturn'); + } else { + $semicolon = $phpcsFile->findNext(T_WHITESPACE, ($returnToken + 1), null, true); + if ($tokens[$semicolon]['code'] === T_SEMICOLON) { + $error = 'Function return type is not void, but function is returning void here'; + $phpcsFile->addError($error, $returnToken, 'InvalidReturnNotVoid'); + } + } + } + }//end if + }//end if + } else { + $error = 'Missing @return tag in function comment'; + $phpcsFile->addError($error, $tokens[$commentStart]['comment_closer'], 'MissingReturn'); + }//end if + + }//end processReturn() + + + /** + * Process any throw tags that this function comment has. + * + * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned. + * @param int $stackPtr The position of the current token + * in the stack passed in $tokens. + * @param int $commentStart The position in the stack where the comment started. + * + * @return void + */ + protected function processThrows(File $phpcsFile, $stackPtr, $commentStart) + { + $tokens = $phpcsFile->getTokens(); + + $throws = array(); + foreach ($tokens[$commentStart]['comment_tags'] as $pos => $tag) { + if ($tokens[$tag]['content'] !== '@throws') { + continue; + } + + $exception = null; + $comment = null; + if ($tokens[($tag + 2)]['code'] === T_DOC_COMMENT_STRING) { + $matches = array(); + preg_match('/([^\s]+)(?:\s+(.*))?/', $tokens[($tag + 2)]['content'], $matches); + $exception = $matches[1]; + if (isset($matches[2]) === true && trim($matches[2]) !== '') { + $comment = $matches[2]; + } + } + + if ($exception === null) { + $error = 'Exception type and comment missing for @throws tag in function comment'; + $phpcsFile->addError($error, $tag, 'InvalidThrows'); + } else if ($comment === null) { + $error = 'Comment missing for @throws tag in function comment'; +// $phpcsFile->addError($error, $tag, 'EmptyThrows'); + } else { + // Any strings until the next tag belong to this comment. + if (isset($tokens[$commentStart]['comment_tags'][($pos + 1)]) === true) { + $end = $tokens[$commentStart]['comment_tags'][($pos + 1)]; + } else { + $end = $tokens[$commentStart]['comment_closer']; + } + + for ($i = ($tag + 3); $i < $end; $i++) { + if ($tokens[$i]['code'] === T_DOC_COMMENT_STRING) { + $comment .= ' '.$tokens[$i]['content']; + } + } + + // Starts with a capital letter and ends with a fullstop. + $firstChar = $comment{0}; + if (strtoupper($firstChar) !== $firstChar) { + $error = '@throws tag comment must start with a capital letter'; + $phpcsFile->addError($error, ($tag + 2), 'ThrowsNotCapital'); + } + + $lastChar = substr($comment, -1); + if ($lastChar !== '.') { + $error = '@throws tag comment must end with a full stop'; + $phpcsFile->addError($error, ($tag + 2), 'ThrowsNoFullStop'); + } + }//end if + }//end foreach + + }//end processThrows() + + + /** + * Process the function parameter comments. + * + * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned. + * @param int $stackPtr The position of the current token + * in the stack passed in $tokens. + * @param int $commentStart The position in the stack where the comment started. + * + * @return void + */ + protected function processParams(File $phpcsFile, $stackPtr, $commentStart) + { + if ($this->phpVersion === null) { + $this->phpVersion = Config::getConfigData('php_version'); + if ($this->phpVersion === null) { + $this->phpVersion = PHP_VERSION_ID; + } + } + + $tokens = $phpcsFile->getTokens(); + + $params = array(); + $maxType = 0; + $maxVar = 0; + foreach ($tokens[$commentStart]['comment_tags'] as $pos => $tag) { + if ($tokens[$tag]['content'] !== '@param') { + continue; + } + + $type = ''; + $typeSpace = 0; + $var = ''; + $varSpace = 0; + $comment = ''; + $commentLines = array(); + if ($tokens[($tag + 2)]['code'] === T_DOC_COMMENT_STRING) { + $matches = array(); + preg_match('/([^$&.]+)(?:((?:\.\.\.)?(?:\$|&)[^\s]+)(?:(\s+)(.*))?)?/', $tokens[($tag + 2)]['content'], $matches); + + if (empty($matches) === false) { + $typeLen = strlen($matches[1]); + $type = trim($matches[1]); + $typeSpace = ($typeLen - strlen($type)); + $typeLen = strlen($type); + if ($typeLen > $maxType) { + $maxType = $typeLen; + } + } + + if (isset($matches[2]) === true) { + $var = $matches[2]; + $varLen = strlen($var); + if ($varLen > $maxVar) { + $maxVar = $varLen; + } + + if (isset($matches[4]) === true) { + $varSpace = strlen($matches[3]); + $comment = $matches[4]; + $commentLines[] = array( + 'comment' => $comment, + 'token' => ($tag + 2), + 'indent' => $varSpace, + ); + + // Any strings until the next tag belong to this comment. + if (isset($tokens[$commentStart]['comment_tags'][($pos + 1)]) === true) { + $end = $tokens[$commentStart]['comment_tags'][($pos + 1)]; + } else { + $end = $tokens[$commentStart]['comment_closer']; + } + + for ($i = ($tag + 3); $i < $end; $i++) { + if ($tokens[$i]['code'] === T_DOC_COMMENT_STRING) { + $indent = 0; + if ($tokens[($i - 1)]['code'] === T_DOC_COMMENT_WHITESPACE) { + $indent = strlen($tokens[($i - 1)]['content']); + } + + $comment .= ' '.$tokens[$i]['content']; + $commentLines[] = array( + 'comment' => $tokens[$i]['content'], + 'token' => $i, + 'indent' => $indent, + ); + } + } + } else { + $error = 'Missing parameter comment'; +// $phpcsFile->addError($error, $tag, 'MissingParamComment'); + $commentLines[] = array('comment' => ''); + }//end if + } else { + $error = 'Missing parameter name'; + $phpcsFile->addError($error, $tag, 'MissingParamName'); + }//end if + } else { + $error = 'Missing parameter type'; + $phpcsFile->addError($error, $tag, 'MissingParamType'); + }//end if + + $params[] = array( + 'tag' => $tag, + 'type' => $type, + 'var' => $var, + 'comment' => $comment, + 'commentLines' => $commentLines, + 'type_space' => $typeSpace, + 'var_space' => $varSpace, + ); + }//end foreach + + $realParams = $phpcsFile->getMethodParameters($stackPtr); + $foundParams = array(); + + // We want to use ... for all variable length arguments, so added + // this prefix to the variable name so comparisons are easier. + foreach ($realParams as $pos => $param) { + if ($param['variable_length'] === true) { + $realParams[$pos]['name'] = '...'.$realParams[$pos]['name']; + } + } + + foreach ($params as $pos => $param) { + // If the type is empty, the whole line is empty. + if ($param['type'] === '') { + continue; + } + + // Check the param type value. + $typeNames = explode('|', $param['type']); + $suggestedTypeNames = array(); + + foreach ($typeNames as $typeName) { + $suggestedName = Common::suggestType($typeName); + $suggestedTypeNames[] = $suggestedName; + + if (count($typeNames) > 1) { + continue; + } + + // Check type hint for array and custom type. + $suggestedTypeHint = ''; + if (strpos($suggestedName, 'array') !== false || substr($suggestedName, -2) === '[]') { + $suggestedTypeHint = 'array'; + } else if (strpos($suggestedName, 'callable') !== false) { + $suggestedTypeHint = 'callable'; + } else if (strpos($suggestedName, 'callback') !== false) { + $suggestedTypeHint = 'callable'; + } else if (in_array($suggestedName, Common::$allowedTypes) === false) { + $suggestedTypeHint = $suggestedName; + } + + if ($this->phpVersion >= 70000) { + if ($suggestedName === 'string') { + $suggestedTypeHint = 'string'; + } else if ($suggestedName === 'int' || $suggestedName === 'integer') { + $suggestedTypeHint = 'int'; + } else if ($suggestedName === 'float') { + $suggestedTypeHint = 'float'; + } else if ($suggestedName === 'bool' || $suggestedName === 'boolean') { + $suggestedTypeHint = 'bool'; + } + } + + if ($suggestedTypeHint !== '' && isset($realParams[$pos]) === true) { + $typeHint = $realParams[$pos]['type_hint']; + if ($typeHint === '') { + $error = 'Type hint "%s" missing for %s'; + $data = array( + $suggestedTypeHint, + $param['var'], + ); + + $errorCode = 'TypeHintMissing'; + if ($suggestedTypeHint === 'string' + || $suggestedTypeHint === 'int' + || $suggestedTypeHint === 'float' + || $suggestedTypeHint === 'bool' + ) { + $errorCode = 'Scalar'.$errorCode; + } + +// $phpcsFile->addError($error, $stackPtr, $errorCode, $data); + } else if ($typeHint !== substr($suggestedTypeHint, (strlen($typeHint) * -1))) { + $error = 'Expected type hint "%s"; found "%s" for %s'; + $data = array( + $suggestedTypeHint, + $typeHint, + $param['var'], + ); + $phpcsFile->addError($error, $stackPtr, 'IncorrectTypeHint', $data); + }//end if + } else if ($suggestedTypeHint === '' && isset($realParams[$pos]) === true) { + $typeHint = $realParams[$pos]['type_hint']; + if ($typeHint !== '') { + $error = 'Unknown type hint "%s" found for %s'; + $data = array( + $typeHint, + $param['var'], + ); + $phpcsFile->addError($error, $stackPtr, 'InvalidTypeHint', $data); + } + }//end if + }//end foreach + + $suggestedType = implode($suggestedTypeNames, '|'); + if ($param['type'] !== $suggestedType) { + $error = 'Expected "%s" but found "%s" for parameter type'; + $data = array( + $suggestedType, + $param['type'], + ); + + $fix = $phpcsFile->addFixableError($error, $param['tag'], 'IncorrectParamVarName', $data); + if ($fix === true) { + $phpcsFile->fixer->beginChangeset(); + + $content = $suggestedType; + $content .= str_repeat(' ', $param['type_space']); + $content .= $param['var']; + $content .= str_repeat(' ', $param['var_space']); + if (isset($param['commentLines'][0]) === true) { + $content .= $param['commentLines'][0]['comment']; + } + + $phpcsFile->fixer->replaceToken(($param['tag'] + 2), $content); + + // Fix up the indent of additional comment lines. + foreach ($param['commentLines'] as $lineNum => $line) { + if ($lineNum === 0 + || $param['commentLines'][$lineNum]['indent'] === 0 + ) { + continue; + } + + $diff = (strlen($param['type']) - strlen($suggestedType)); + $newIndent = ($param['commentLines'][$lineNum]['indent'] - $diff); + $phpcsFile->fixer->replaceToken( + ($param['commentLines'][$lineNum]['token'] - 1), + str_repeat(' ', $newIndent) + ); + } + + $phpcsFile->fixer->endChangeset(); + }//end if + }//end if + + if ($param['var'] === '') { + continue; + } + + $foundParams[] = $param['var']; + + // Check number of spaces after the type. + $this->checkSpacingAfterParamType($phpcsFile, $param, $maxType); + + // Make sure the param name is correct. + if (isset($realParams[$pos]) === true) { + $realName = $realParams[$pos]['name']; + if ($realName !== $param['var']) { + $code = 'ParamNameNoMatch'; + $data = array( + $param['var'], + $realName, + ); + + $error = 'Doc comment for parameter %s does not match '; + if (strtolower($param['var']) === strtolower($realName)) { + $error .= 'case of '; + $code = 'ParamNameNoCaseMatch'; + } + + $error .= 'actual variable name %s'; + + $phpcsFile->addError($error, $param['tag'], $code, $data); + } + } else if (substr($param['var'], -4) !== ',...') { + // We must have an extra parameter comment. + $error = 'Superfluous parameter comment'; + $phpcsFile->addError($error, $param['tag'], 'ExtraParamComment'); + }//end if + + if ($param['comment'] === '') { + continue; + } + + // Check number of spaces after the var name. + $this->checkSpacingAfterParamName($phpcsFile, $param, $maxVar); + + // Param comments must start with a capital letter and end with the full stop. + if (preg_match('/^(\p{Ll}|\P{L})/u', $param['comment']) === 1) { + $error = 'Parameter comment must start with a capital letter'; + $phpcsFile->addError($error, $param['tag'], 'ParamCommentNotCapital'); + } + + $lastChar = substr($param['comment'], -1); + if ($lastChar !== '.') { + $error = 'Parameter comment must end with a full stop'; + $phpcsFile->addError($error, $param['tag'], 'ParamCommentFullStop'); + } + }//end foreach + + $realNames = array(); + foreach ($realParams as $realParam) { + $realNames[] = $realParam['name']; + } + + // Report missing comments. + $diff = array_diff($realNames, $foundParams); + foreach ($diff as $neededParam) { + $error = 'Doc comment for parameter "%s" missing'; + $data = array($neededParam); +// $phpcsFile->addError($error, $commentStart, 'MissingParamTag', $data); + } + + }//end processParams() + + + /** + * Check the spacing after the type of a parameter. + * + * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned. + * @param array $param The parameter to be checked. + * @param int $maxType The maxlength of the longest parameter type. + * @param int $spacing The number of spaces to add after the type. + * + * @return void + */ + protected function checkSpacingAfterParamType(File $phpcsFile, $param, $maxType, $spacing=1) + { + // Check number of spaces after the type. + $spaces = ($maxType - strlen($param['type']) + $spacing); + if ($param['type_space'] !== $spaces) { + $error = 'Expected %s spaces after parameter type; %s found'; + $data = array( + $spaces, + $param['type_space'], + ); + + $fix = $phpcsFile->addFixableError($error, $param['tag'], 'SpacingAfterParamType', $data); + if ($fix === true) { + $phpcsFile->fixer->beginChangeset(); + + $content = $param['type']; + $content .= str_repeat(' ', $spaces); + $content .= $param['var']; + $content .= str_repeat(' ', $param['var_space']); + $content .= $param['commentLines'][0]['comment']; + $phpcsFile->fixer->replaceToken(($param['tag'] + 2), $content); + + // Fix up the indent of additional comment lines. + foreach ($param['commentLines'] as $lineNum => $line) { + if ($lineNum === 0 + || $param['commentLines'][$lineNum]['indent'] === 0 + ) { + continue; + } + + $diff = ($param['type_space'] - $spaces); + $newIndent = ($param['commentLines'][$lineNum]['indent'] - $diff); + $phpcsFile->fixer->replaceToken( + ($param['commentLines'][$lineNum]['token'] - 1), + str_repeat(' ', $newIndent) + ); + } + + $phpcsFile->fixer->endChangeset(); + }//end if + }//end if + + }//end checkSpacingAfterParamType() + + + /** + * Check the spacing after the name of a parameter. + * + * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned. + * @param array $param The parameter to be checked. + * @param int $maxVar The maxlength of the longest parameter name. + * @param int $spacing The number of spaces to add after the type. + * + * @return void + */ + protected function checkSpacingAfterParamName(File $phpcsFile, $param, $maxVar, $spacing=1) + { + // Check number of spaces after the var name. + $spaces = ($maxVar - strlen($param['var']) + $spacing); + if ($param['var_space'] !== $spaces) { + $error = 'Expected %s spaces after parameter name; %s found'; + $data = array( + $spaces, + $param['var_space'], + ); + + $fix = $phpcsFile->addFixableError($error, $param['tag'], 'SpacingAfterParamName', $data); + if ($fix === true) { + $phpcsFile->fixer->beginChangeset(); + + $content = $param['type']; + $content .= str_repeat(' ', $param['type_space']); + $content .= $param['var']; + $content .= str_repeat(' ', $spaces); + $content .= $param['commentLines'][0]['comment']; + $phpcsFile->fixer->replaceToken(($param['tag'] + 2), $content); + + // Fix up the indent of additional comment lines. + foreach ($param['commentLines'] as $lineNum => $line) { + if ($lineNum === 0 + || $param['commentLines'][$lineNum]['indent'] === 0 + ) { + continue; + } + + $diff = ($param['var_space'] - $spaces); + $newIndent = ($param['commentLines'][$lineNum]['indent'] - $diff); + $phpcsFile->fixer->replaceToken( + ($param['commentLines'][$lineNum]['token'] - 1), + str_repeat(' ', $newIndent) + ); + } + + $phpcsFile->fixer->endChangeset(); + }//end if + }//end if + + }//end checkSpacingAfterParamName() + + +}//end class diff --git a/dev/tests/static/Magento/Sniffs/Commenting/VariableCommentSniff.php b/dev/tests/static/Magento/Sniffs/Commenting/VariableCommentSniff.php new file mode 100644 index 000000000..2a3cb92b2 --- /dev/null +++ b/dev/tests/static/Magento/Sniffs/Commenting/VariableCommentSniff.php @@ -0,0 +1,153 @@ +<?php +/** + * Parses and verifies the variable doc comment. + * + * @author Greg Sherwood <gsherwood@squiz.net> + * @copyright 2006-2015 Squiz Pty Ltd (ABN 77 084 670 600) + * @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence + */ + +namespace Magento\Sniffs\Commenting; + +use PHP_CodeSniffer\Sniffs\AbstractVariableSniff; +use PHP_CodeSniffer\Files\File; +use PHP_CodeSniffer\Util\Common; + +class VariableCommentSniff extends AbstractVariableSniff +{ + + + /** + * Called to process class member vars. + * + * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned. + * @param int $stackPtr The position of the current token + * in the stack passed in $tokens. + * + * @return void + */ + public function processMemberVar(File $phpcsFile, $stackPtr) + { + $tokens = $phpcsFile->getTokens(); + $ignore = array( + T_PUBLIC, + T_PRIVATE, + T_PROTECTED, + T_VAR, + T_STATIC, + T_WHITESPACE, + ); + + $commentEnd = $phpcsFile->findPrevious($ignore, ($stackPtr - 1), null, true); + if ($commentEnd === false + || ($tokens[$commentEnd]['code'] !== T_DOC_COMMENT_CLOSE_TAG + && $tokens[$commentEnd]['code'] !== T_COMMENT) + ) { + $phpcsFile->addError('Missing member variable doc comment', $stackPtr, 'Missing'); + return; + } + + if ($tokens[$commentEnd]['code'] === T_COMMENT) { + $phpcsFile->addError('You must use "/**" style comments for a member variable comment', $stackPtr, 'WrongStyle'); + return; + } + + $commentStart = $tokens[$commentEnd]['comment_opener']; + + $foundVar = null; + foreach ($tokens[$commentStart]['comment_tags'] as $tag) { + if ($tokens[$tag]['content'] === '@var') { + if ($foundVar !== null) { + $error = 'Only one @var tag is allowed in a member variable comment'; + $phpcsFile->addError($error, $tag, 'DuplicateVar'); + } else { + $foundVar = $tag; + } + } else if ($tokens[$tag]['content'] === '@see') { + // Make sure the tag isn't empty. + $string = $phpcsFile->findNext(T_DOC_COMMENT_STRING, $tag, $commentEnd); + if ($string === false || $tokens[$string]['line'] !== $tokens[$tag]['line']) { + $error = 'Content missing for @see tag in member variable comment'; + $phpcsFile->addError($error, $tag, 'EmptySees'); + } + } else { + $error = '%s tag is not allowed in member variable comment'; + $data = array($tokens[$tag]['content']); + $phpcsFile->addWarning($error, $tag, 'TagNotAllowed', $data); + }//end if + }//end foreach + + // The @var tag is the only one we require. + if ($foundVar === null) { + $error = 'Missing @var tag in member variable comment'; + $phpcsFile->addError($error, $commentEnd, 'MissingVar'); + return; + } + + $firstTag = $tokens[$commentStart]['comment_tags'][0]; + if ($foundVar !== null && $tokens[$firstTag]['content'] !== '@var') { + $error = 'The @var tag must be the first tag in a member variable comment'; + $phpcsFile->addError($error, $foundVar, 'VarOrder'); + } + + // Make sure the tag isn't empty and has the correct padding. + $string = $phpcsFile->findNext(T_DOC_COMMENT_STRING, $foundVar, $commentEnd); + if ($string === false || $tokens[$string]['line'] !== $tokens[$foundVar]['line']) { + $error = 'Content missing for @var tag in member variable comment'; + $phpcsFile->addError($error, $foundVar, 'EmptyVar'); + return; + } + + $varType = $tokens[($foundVar + 2)]['content']; + $suggestedType = Common::suggestType($varType); + if ($varType !== $suggestedType) { + $error = 'Expected "%s" but found "%s" for @var tag in member variable comment'; + $data = array( + $suggestedType, + $varType, + ); + + $fix = $phpcsFile->addFixableError($error, ($foundVar + 2), 'IncorrectVarType', $data); + if ($fix === true) { + $phpcsFile->fixer->replaceToken(($foundVar + 2), $suggestedType); + } + } + + }//end processMemberVar() + + + /** + * Called to process a normal variable. + * + * Not required for this sniff. + * + * @param \PHP_CodeSniffer\Files\File $phpcsFile The PHP_CodeSniffer file where this token was found. + * @param int $stackPtr The position where the double quoted + * string was found. + * + * @return void + */ + protected function processVariable(File $phpcsFile, $stackPtr) + { + + }//end processVariable() + + + /** + * Called to process variables found in double quoted strings. + * + * Not required for this sniff. + * + * @param \PHP_CodeSniffer\Files\File $phpcsFile The PHP_CodeSniffer file where this token was found. + * @param int $stackPtr The position where the double quoted + * string was found. + * + * @return void + */ + protected function processVariableInString(File $phpcsFile, $stackPtr) + { + + }//end processVariableInString() + + +}//end class diff --git a/dev/tests/static/Magento/Sniffs/Files/LineLengthSniff.php b/dev/tests/static/Magento/Sniffs/Files/LineLengthSniff.php index 50020cb29..2abcf0531 100644 --- a/dev/tests/static/Magento/Sniffs/Files/LineLengthSniff.php +++ b/dev/tests/static/Magento/Sniffs/Files/LineLengthSniff.php @@ -5,10 +5,12 @@ */ namespace Magento\Sniffs\Files; +use PHP_CodeSniffer\Standards\Generic\Sniffs\Files\LineLengthSniff as FilesLineLengthSniff; + /** * Line length sniff which ignores long lines in case they contain strings intended for translation. */ -class LineLengthSniff extends \Generic_Sniffs_Files_LineLengthSniff +class LineLengthSniff extends FilesLineLengthSniff { /** * Having previous line content allows to ignore long lines in case of multi-line declaration. @@ -20,7 +22,7 @@ class LineLengthSniff extends \Generic_Sniffs_Files_LineLengthSniff /** * {@inheritdoc} */ - protected function checkLineLength(\PHP_CodeSniffer_File $phpcsFile, $stackPtr, $lineContent) + protected function checkLineLength($phpcsFile, $stackPtr, $lineContent) { $previousLineRegexp = '~__\($|\bPhrase\($~'; $currentLineRegexp = '~__\(.+\)|\bPhrase\(.+\)~'; diff --git a/dev/tests/static/Magento/Sniffs/LiteralNamespaces/LiteralNamespacesSniff.php b/dev/tests/static/Magento/Sniffs/LiteralNamespaces/LiteralNamespacesSniff.php index 31796ec4e..b6af3f37c 100644 --- a/dev/tests/static/Magento/Sniffs/LiteralNamespaces/LiteralNamespacesSniff.php +++ b/dev/tests/static/Magento/Sniffs/LiteralNamespaces/LiteralNamespacesSniff.php @@ -5,13 +5,13 @@ */ namespace Magento\Sniffs\LiteralNamespaces; -use PHP_CodeSniffer_File; -use PHP_CodeSniffer_Sniff; +use PHP_CodeSniffer\Sniffs\Sniff; +use PHP_CodeSniffer\Files\File; /** * Custom phpcs sniff to detect usages of literal class and interface names. */ -class LiteralNamespacesSniff implements PHP_CodeSniffer_Sniff +class LiteralNamespacesSniff implements Sniff { /** * @var string @@ -37,7 +37,7 @@ public function register() /** * @inheritdoc */ - public function process(PHP_CodeSniffer_File $sourceFile, $stackPtr) + public function process(File $sourceFile, $stackPtr) { $tokens = $sourceFile->getTokens(); if ($sourceFile->findPrevious(T_STRING_CONCAT, $stackPtr, $stackPtr - 3) || @@ -47,8 +47,17 @@ public function process(PHP_CodeSniffer_File $sourceFile, $stackPtr) } $content = trim($tokens[$stackPtr]['content'], "\"'"); + // replace double slashes from class name for avoiding problems with class autoload + if (strpos($content, '\\') !== false) { + $content = preg_replace('|\\\{2,}|', '\\', $content); + } + if (preg_match($this->literalNamespacePattern, $content) === 1 && $this->classExists($content)) { - $sourceFile->addError("Use ::class notation instead.", $stackPtr); + $sourceFile->addError( + "Use ::class notation instead.", + $stackPtr, + 'LiteralClassUsage' + ); } } diff --git a/dev/tests/static/Magento/Sniffs/MicroOptimizations/IsNullSniff.php b/dev/tests/static/Magento/Sniffs/MicroOptimizations/IsNullSniff.php index 095025a3d..928fc3a0d 100644 --- a/dev/tests/static/Magento/Sniffs/MicroOptimizations/IsNullSniff.php +++ b/dev/tests/static/Magento/Sniffs/MicroOptimizations/IsNullSniff.php @@ -5,10 +5,10 @@ */ namespace Magento\Sniffs\MicroOptimizations; -use PHP_CodeSniffer_File; -use PHP_CodeSniffer_Sniff; +use PHP_CodeSniffer\Sniffs\Sniff; +use PHP_CodeSniffer\Files\File; -class IsNullSniff implements PHP_CodeSniffer_Sniff +class IsNullSniff implements Sniff { /** * @var string @@ -26,11 +26,15 @@ public function register() /** * @inheritdoc */ - public function process(PHP_CodeSniffer_File $sourceFile, $stackPtr) + public function process(File $sourceFile, $stackPtr) { $tokens = $sourceFile->getTokens(); if ($tokens[$stackPtr]['content'] === $this->blacklist) { - $sourceFile->addError("is_null must be avoided. Use strict comparison instead.", $stackPtr); + $sourceFile->addError( + "is_null must be avoided. Use strict comparison instead.", + $stackPtr, + 'IsNullUsage' + ); } } } diff --git a/dev/tests/static/Magento/Sniffs/NamingConventions/InterfaceNameSniff.php b/dev/tests/static/Magento/Sniffs/NamingConventions/InterfaceNameSniff.php index 2e3e4db2a..1618beb66 100644 --- a/dev/tests/static/Magento/Sniffs/NamingConventions/InterfaceNameSniff.php +++ b/dev/tests/static/Magento/Sniffs/NamingConventions/InterfaceNameSniff.php @@ -5,10 +5,10 @@ */ namespace Magento\Sniffs\NamingConventions; -use PHP_CodeSniffer_File; -use PHP_CodeSniffer_Sniff; +use PHP_CodeSniffer\Sniffs\Sniff; +use PHP_CodeSniffer\Files\File; -class InterfaceNameSniff implements PHP_CodeSniffer_Sniff +class InterfaceNameSniff implements Sniff { const INTERFACE_SUFFIX = 'Interface'; @@ -23,7 +23,7 @@ public function register() /** * {@inheritdoc} */ - public function process(PHP_CodeSniffer_File $sourceFile, $stackPtr) + public function process(File $sourceFile, $stackPtr) { $tokens = $sourceFile->getTokens(); $declarationLine = $tokens[$stackPtr]['line']; @@ -32,7 +32,11 @@ public function process(PHP_CodeSniffer_File $sourceFile, $stackPtr) while ($tokens[$stackPtr]['line'] == $declarationLine) { if ($tokens[$stackPtr]['type'] == 'T_STRING') { if (substr($tokens[$stackPtr]['content'], 0 - $suffixLength) != self::INTERFACE_SUFFIX) { - $sourceFile->addError('Interface should have name that ends with "Interface" suffix.', $stackPtr); + $sourceFile->addError( + 'Interface should have name that ends with "Interface" suffix.', + $stackPtr, + 'WrongInterfaceName' + ); } break; } diff --git a/dev/tests/static/Magento/Sniffs/NamingConventions/ReservedWordsSniff.php b/dev/tests/static/Magento/Sniffs/NamingConventions/ReservedWordsSniff.php index 3d2d979c3..f41c235a6 100644 --- a/dev/tests/static/Magento/Sniffs/NamingConventions/ReservedWordsSniff.php +++ b/dev/tests/static/Magento/Sniffs/NamingConventions/ReservedWordsSniff.php @@ -5,10 +5,10 @@ */ namespace Magento\Sniffs\NamingConventions; -use PHP_CodeSniffer_File; -use PHP_CodeSniffer_Sniff; +use PHP_CodeSniffer\Sniffs\Sniff; +use PHP_CodeSniffer\Files\File; -class ReservedWordsSniff implements PHP_CodeSniffer_Sniff +class ReservedWordsSniff implements Sniff { /** * The following words cannot be used to name a class, interface or trait, @@ -45,11 +45,11 @@ public function register() /** * Check all namespace parts * - * @param PHP_CodeSniffer_File $sourceFile + * @param File $sourceFile * @param int $stackPtr * @return void */ - protected function validateNamespace(PHP_CodeSniffer_File $sourceFile, $stackPtr) + protected function validateNamespace(File $sourceFile, $stackPtr) { $stackPtr += 2; $tokens = $sourceFile->getTokens(); @@ -74,11 +74,11 @@ protected function validateNamespace(PHP_CodeSniffer_File $sourceFile, $stackPtr /** * Check class name not having reserved words * - * @param PHP_CodeSniffer_File $sourceFile + * @param File $sourceFile * @param int $stackPtr * @return void */ - protected function validateClass(PHP_CodeSniffer_File $sourceFile, $stackPtr) + protected function validateClass(File $sourceFile, $stackPtr) { $tokens = $sourceFile->getTokens(); $stackPtr += 2; //skip "class" and whitespace @@ -96,7 +96,7 @@ protected function validateClass(PHP_CodeSniffer_File $sourceFile, $stackPtr) /** * {@inheritdoc} */ - public function process(PHP_CodeSniffer_File $sourceFile, $stackPtr) + public function process(File $sourceFile, $stackPtr) { $tokens = $sourceFile->getTokens(); switch ($tokens[$stackPtr]['code']) { diff --git a/dev/tests/static/Magento/Sniffs/Whitespace/EmptyLineMissedSniff.php b/dev/tests/static/Magento/Sniffs/Whitespace/EmptyLineMissedSniff.php index 41782b1e2..de3cfc50b 100644 --- a/dev/tests/static/Magento/Sniffs/Whitespace/EmptyLineMissedSniff.php +++ b/dev/tests/static/Magento/Sniffs/Whitespace/EmptyLineMissedSniff.php @@ -5,13 +5,13 @@ */ namespace Magento\Sniffs\Whitespace; -use PHP_CodeSniffer_File; -use PHP_CodeSniffer_Sniff; +use PHP_CodeSniffer\Sniffs\Sniff; +use PHP_CodeSniffer\Files\File; /** * Class EmptyLineMissedSniff */ -class EmptyLineMissedSniff implements PHP_CodeSniffer_Sniff +class EmptyLineMissedSniff implements Sniff { /** * {@inheritdoc} @@ -24,7 +24,7 @@ public function register() /** * {@inheritdoc} */ - public function process(PHP_CodeSniffer_File $phpcsFile, $stackPtr) + public function process(File $phpcsFile, $stackPtr) { $tokens = $phpcsFile->getTokens(); if ($this->doCheck($phpcsFile, $stackPtr, $tokens)) { @@ -37,12 +37,12 @@ public function process(PHP_CodeSniffer_File $phpcsFile, $stackPtr) } /** - * @param PHP_CodeSniffer_File $phpcsFile + * @param File $phpcsFile * @param int $stackPtr * @param array $tokens * @return bool */ - private function doCheck(PHP_CodeSniffer_File $phpcsFile, $stackPtr, $tokens) + private function doCheck(File $phpcsFile, $stackPtr, $tokens) { $result = false; if ($phpcsFile->hasCondition($stackPtr, T_CLASS) || $phpcsFile->hasCondition($stackPtr, T_INTERFACE)) { diff --git a/dev/tests/static/Magento/Sniffs/Whitespace/MultipleEmptyLinesSniff.php b/dev/tests/static/Magento/Sniffs/Whitespace/MultipleEmptyLinesSniff.php index 794f316de..f276426ef 100644 --- a/dev/tests/static/Magento/Sniffs/Whitespace/MultipleEmptyLinesSniff.php +++ b/dev/tests/static/Magento/Sniffs/Whitespace/MultipleEmptyLinesSniff.php @@ -5,13 +5,13 @@ */ namespace Magento\Sniffs\Whitespace; -use PHP_CodeSniffer_File; -use PHP_CodeSniffer_Sniff; +use PHP_CodeSniffer\Sniffs\Sniff; +use PHP_CodeSniffer\Files\File; /** * Class MultipleEmptyLinesSniff */ -class MultipleEmptyLinesSniff implements PHP_CodeSniffer_Sniff +class MultipleEmptyLinesSniff implements Sniff { /** * {@inheritdoc} @@ -24,7 +24,7 @@ public function register() /** * {@inheritdoc} */ - public function process(PHP_CodeSniffer_File $phpcsFile, $stackPtr) + public function process(File $phpcsFile, $stackPtr) { $tokens = $phpcsFile->getTokens(); if ($phpcsFile->hasCondition($stackPtr, T_FUNCTION) diff --git a/dev/tests/static/Magento/ruleset.xml b/dev/tests/static/Magento/ruleset.xml index 794b5a8ff..98b4d4241 100644 --- a/dev/tests/static/Magento/ruleset.xml +++ b/dev/tests/static/Magento/ruleset.xml @@ -19,6 +19,10 @@ <rule ref="Magento.LiteralNamespaces.LiteralNamespaces"> <exclude-pattern>*/_files/*</exclude-pattern> </rule> + <rule ref="Magento.Commenting.FunctionComment"> + <exclude-pattern>*/dev/tests*</exclude-pattern> + </rule> + <rule ref="Magento.Commenting.VariableComment"/> <rule ref="Generic.Functions.CallTimePassByReference"/> <rule ref="Generic.PHP.DeprecatedFunctions"/> @@ -26,4 +30,9 @@ <rule ref="Squiz.Commenting.DocCommentAlignment"/> <rule ref="Squiz.Functions.GlobalFunction"/> <rule ref="Squiz.WhiteSpace.LogicalOperatorSpacing"/> + + <!-- Codeception Webdriver violates this, cannot fix as we extend from them --> + <rule ref="PSR2.Methods.MethodDeclaration.Underscore"> + <severity>0</severity> + </rule> </ruleset> diff --git a/dev/tests/unit/Magento/FunctionalTestFramework/DataGenerator/Handlers/OperationDefinitionObjectHandlerTest.php b/dev/tests/unit/Magento/FunctionalTestFramework/DataGenerator/Handlers/OperationDefinitionObjectHandlerTest.php index ffb15648a..b54980314 100644 --- a/dev/tests/unit/Magento/FunctionalTestFramework/DataGenerator/Handlers/OperationDefinitionObjectHandlerTest.php +++ b/dev/tests/unit/Magento/FunctionalTestFramework/DataGenerator/Handlers/OperationDefinitionObjectHandlerTest.php @@ -62,7 +62,7 @@ public function testGetMultipleObjects() OperationDefinitionObjectHandler::ENTITY_OPERATION_ENTRY_VALUE => "integer" ], ] - ]]]; + ]]]; $this->setMockParserOutput($mockData); //Perform Assertions @@ -70,7 +70,6 @@ public function testGetMultipleObjects() $operations = $operationDefinitionManager->getAllObjects(); $this->assertArrayHasKey($operationType1 . $dataType1, $operations); $this->assertArrayHasKey($operationType2 . $dataType1, $operations); - } public function testObjectCreation() diff --git a/dev/tests/unit/Magento/FunctionalTestFramework/DataGenerator/Objects/EntityDataObjectTest.php b/dev/tests/unit/Magento/FunctionalTestFramework/DataGenerator/Objects/EntityDataObjectTest.php index 0545fe455..5b63f0c94 100644 --- a/dev/tests/unit/Magento/FunctionalTestFramework/DataGenerator/Objects/EntityDataObjectTest.php +++ b/dev/tests/unit/Magento/FunctionalTestFramework/DataGenerator/Objects/EntityDataObjectTest.php @@ -7,6 +7,7 @@ namespace Magento\FunctionalTestingFramework\DataGenerator\Objects; use Magento\FunctionalTestingFramework\Util\MagentoTestCase; +use Magento\FunctionalTestingFramework\Exceptions\TestFrameworkException; use tests\unit\Util\TestLoggingUtil; /** @@ -87,12 +88,11 @@ public function testVarGetter() $dataObject = new EntityDataObject("name", "type", $data, null, null, $vars); // Perform Asserts $this->assertEquals("id", $dataObject->getVarReference("someOtherEntity")); - } public function testGetDataByNameInvalidUniquenessFormatValue() { - $this->expectException("Magento\FunctionalTestingFramework\Exceptions\TestFrameworkException"); + $this->expectException(TestFrameworkException::class); $data = ["datakey1" => "value1", "datakey2" => "value2", "datakey3" => "value3"]; $dataObject = new EntityDataObject("name", "type", $data, null, null, null); // Trigger Exception @@ -102,7 +102,7 @@ public function testGetDataByNameInvalidUniquenessFormatValue() public function testUniquenessFunctionsDontExist() { $this->markTestIncomplete('Test fails, as msqMock is always declared in test runs.'); - $this->expectException("Magento\FunctionalTestingFramework\Exceptions\TestFrameworkException"); + $this->expectException(TestFrameworkException::class); $data = ["datakey1" => "value1", "datakey2" => "value2", "datakey3" => "value3"]; $uniquenessKeys = ["datakey1" => "suffix"]; $dataObject = new EntityDataObject("name", "type", $data, null, $uniquenessKeys, null); diff --git a/dev/tests/unit/Magento/FunctionalTestFramework/Test/Objects/ActionObjectTest.php b/dev/tests/unit/Magento/FunctionalTestFramework/Test/Objects/ActionObjectTest.php index 9ad8a01aa..6163b4d38 100644 --- a/dev/tests/unit/Magento/FunctionalTestFramework/Test/Objects/ActionObjectTest.php +++ b/dev/tests/unit/Magento/FunctionalTestFramework/Test/Objects/ActionObjectTest.php @@ -15,6 +15,7 @@ use Magento\FunctionalTestingFramework\Test\Objects\ActionObject; use Magento\FunctionalTestingFramework\Page\Handlers\SectionObjectHandler; use Magento\FunctionalTestingFramework\Page\Objects\SectionObject; +use Magento\FunctionalTestingFramework\Exceptions\TestReferenceException; use tests\unit\Util\TestLoggingUtil; use Magento\FunctionalTestingFramework\Util\MagentoTestCase; @@ -338,7 +339,7 @@ public function testResolveArrayData() */ public function testTooFewArgumentException() { - $this->expectException('Magento\FunctionalTestingFramework\Exceptions\TestReferenceException'); + $this->expectException(TestReferenceException::class); $actionObject = new ActionObject('key123', 'fillField', [ 'selector' => "{{SectionObject.elementObject('arg1')}}", @@ -356,7 +357,7 @@ public function testTooFewArgumentException() */ public function testTooManyArgumentException() { - $this->expectException('Magento\FunctionalTestingFramework\Exceptions\TestReferenceException'); + $this->expectException(TestReferenceException::class); $actionObject = new ActionObject('key123', 'fillField', [ 'selector' => "{{SectionObject.elementObject('arg1', 'arg2', 'arg3')}}", diff --git a/dev/tests/unit/Magento/FunctionalTestFramework/Test/Util/ActionMergeUtilTest.php b/dev/tests/unit/Magento/FunctionalTestFramework/Test/Util/ActionMergeUtilTest.php index 7a9c6c8c9..0f367c226 100644 --- a/dev/tests/unit/Magento/FunctionalTestFramework/Test/Util/ActionMergeUtilTest.php +++ b/dev/tests/unit/Magento/FunctionalTestFramework/Test/Util/ActionMergeUtilTest.php @@ -142,7 +142,7 @@ public function testNoActionException() ActionObject::MERGE_ACTION_ORDER_BEFORE ); - $this->expectException("\Magento\FunctionalTestingFramework\Exceptions\XmlException"); + $this->expectException(\Magento\FunctionalTestingFramework\Exceptions\XmlException::class); $actionMergeUtil = new ActionMergeUtil("actionMergeUtilTest", "TestCase"); $actionMergeUtil->resolveActionSteps($actionObjects); diff --git a/dev/tests/unit/Magento/FunctionalTestFramework/Test/Util/ActionObjectExtractorTest.php b/dev/tests/unit/Magento/FunctionalTestFramework/Test/Util/ActionObjectExtractorTest.php index 1d645ab62..183c9226d 100644 --- a/dev/tests/unit/Magento/FunctionalTestFramework/Test/Util/ActionObjectExtractorTest.php +++ b/dev/tests/unit/Magento/FunctionalTestFramework/Test/Util/ActionObjectExtractorTest.php @@ -44,7 +44,7 @@ public function testBasicActionObjectExtration() public function testInvalidMergeOrderReference() { $invalidArray = $this->createBasicActionObjectArray('invalidTestAction1', 'invalidTestAction1'); - $this->expectException('\Magento\FunctionalTestingFramework\Exceptions\TestReferenceException'); + $this->expectException(\Magento\FunctionalTestingFramework\Exceptions\TestReferenceException::class); try { $this->testActionObjectExtractor->extractActions($invalidArray, 'TestWithSelfReferencingStepKey'); } catch (\Exception $e) { diff --git a/dev/tests/unit/Magento/FunctionalTestFramework/Util/Validation/DuplicateNodeValidationUtilTest.php b/dev/tests/unit/Magento/FunctionalTestFramework/Util/Validation/DuplicateNodeValidationUtilTest.php index b2358e27d..ad47b73ba 100644 --- a/dev/tests/unit/Magento/FunctionalTestFramework/Util/Validation/DuplicateNodeValidationUtilTest.php +++ b/dev/tests/unit/Magento/FunctionalTestFramework/Util/Validation/DuplicateNodeValidationUtilTest.php @@ -45,6 +45,5 @@ public function testTestActionValidation() ); $this->expectException(\Exception::class); $exceptionCollector->throwException(); - } } diff --git a/dev/tests/unit/Util/MagentoTestCase.php b/dev/tests/unit/Util/MagentoTestCase.php index eafb5ee31..0aa153106 100644 --- a/dev/tests/unit/Util/MagentoTestCase.php +++ b/dev/tests/unit/Util/MagentoTestCase.php @@ -16,6 +16,7 @@ class MagentoTestCase extends TestCase { /** * Teardown for removing AspectMock Double References + * @return void */ public static function tearDownAfterClass() { diff --git a/dev/tests/unit/Util/OperationDefinitionBuilder.php b/dev/tests/unit/Util/OperationDefinitionBuilder.php index abd5c38f1..63b0fbbfc 100644 --- a/dev/tests/unit/Util/OperationDefinitionBuilder.php +++ b/dev/tests/unit/Util/OperationDefinitionBuilder.php @@ -39,7 +39,7 @@ class OperationDefinitionBuilder /** * Determines if api URL should remove magento_backend_name. - * @var bool + * @var boolean */ private $removeBackend; diff --git a/dev/tests/unit/Util/OperationElementBuilder.php b/dev/tests/unit/Util/OperationElementBuilder.php index cf9322e17..2c2a4d9da 100644 --- a/dev/tests/unit/Util/OperationElementBuilder.php +++ b/dev/tests/unit/Util/OperationElementBuilder.php @@ -23,6 +23,11 @@ class OperationElementBuilder 'isPrimary' => 'boolean' ]; + /** + * Array of nested metadata, merged to main object via addElement() + * + * @var array + */ private $nestedMetadata = []; /** @@ -144,7 +149,7 @@ public function addElements($elementsToAdd) /** * Adds a new set of fields (value => type) into an object parameter to be converted to Operation Elements. * - * @param $fieldsToAdd + * @param array $fieldsToAdd * @return OperationElementBuilder */ public function addFields($fieldsToAdd) diff --git a/dev/tests/unit/Util/TestDataArrayBuilder.php b/dev/tests/unit/Util/TestDataArrayBuilder.php index a8483c26f..aeeeae850 100644 --- a/dev/tests/unit/Util/TestDataArrayBuilder.php +++ b/dev/tests/unit/Util/TestDataArrayBuilder.php @@ -92,7 +92,7 @@ class TestDataArrayBuilder private $testReference = null; /** - * @param $name + * @param string $name * @return $this */ public function withName($name) diff --git a/dev/tests/unit/Util/TestLoggingUtil.php b/dev/tests/unit/Util/TestLoggingUtil.php index eb73196d0..4be16280f 100644 --- a/dev/tests/unit/Util/TestLoggingUtil.php +++ b/dev/tests/unit/Util/TestLoggingUtil.php @@ -71,6 +71,7 @@ public function setMockLoggingUtil() * @param string $type * @param string $message * @param array $context + * @return void */ public function validateMockLogStatement($type, $message, $context) { diff --git a/src/Magento/FunctionalTestingFramework/Allure/Adapter/MagentoAllureAdapter.php b/src/Magento/FunctionalTestingFramework/Allure/Adapter/MagentoAllureAdapter.php index 3d12cb6e1..d23af52da 100644 --- a/src/Magento/FunctionalTestingFramework/Allure/Adapter/MagentoAllureAdapter.php +++ b/src/Magento/FunctionalTestingFramework/Allure/Adapter/MagentoAllureAdapter.php @@ -25,7 +25,7 @@ class MagentoAllureAdapter extends AllureAdapter /** * Array of group values passed to test runner command * - * @return String + * @return string */ private function getGroup() { diff --git a/src/Magento/FunctionalTestingFramework/Config/Converter.php b/src/Magento/FunctionalTestingFramework/Config/Converter.php index 2b046fe5d..dcc67e5cf 100644 --- a/src/Magento/FunctionalTestingFramework/Config/Converter.php +++ b/src/Magento/FunctionalTestingFramework/Config/Converter.php @@ -49,10 +49,10 @@ class Converter implements \Magento\FunctionalTestingFramework\Config\ConverterI /** * Constructor for Converter object. * - * @param ArgumentParser $argumentParser + * @param ArgumentParser $argumentParser * @param InterpreterInterface $argumentInterpreter - * @param string $argumentNodeName - * @param array $idAttributes + * @param string $argumentNodeName + * @param array $idAttributes */ public function __construct( ArgumentParser $argumentParser, @@ -130,7 +130,7 @@ protected function convertXml($elements) * Get key for DOM element * * @param \DOMElement $element - * @return bool|string + * @return boolean|string */ protected function getElementKey(\DOMElement $element) { @@ -149,8 +149,8 @@ protected function getElementKey(\DOMElement $element) * Verify attribute is main key for element. * * @param \DOMElement $element - * @param \DOMAttr $attribute - * @return bool + * @param \DOMAttr $attribute + * @return boolean */ protected function isKeyAttribute(\DOMElement $element, \DOMAttr $attribute) { @@ -200,7 +200,7 @@ protected function getChildNodes(\DOMElement $element) * Cast nodeValue to int or double. * * @param string $nodeValue - * @return float|int + * @return float|integer */ protected function castNumeric($nodeValue) { diff --git a/src/Magento/FunctionalTestingFramework/Config/Converter/Dom/Flat.php b/src/Magento/FunctionalTestingFramework/Config/Converter/Dom/Flat.php index 23f3b3e7c..4350633e7 100644 --- a/src/Magento/FunctionalTestingFramework/Config/Converter/Dom/Flat.php +++ b/src/Magento/FunctionalTestingFramework/Config/Converter/Dom/Flat.php @@ -66,7 +66,7 @@ protected function getNodeAttributes(\DOMNode $node) * ) * * @param \DOMNode $source - * @param string $basePath + * @param string $basePath * @return string|array * @throws \UnexpectedValueException * @SuppressWarnings(PHPMD.CyclomaticComplexity) diff --git a/src/Magento/FunctionalTestingFramework/Config/Dom.php b/src/Magento/FunctionalTestingFramework/Config/Dom.php index 23ec6d2eb..1305574cc 100644 --- a/src/Magento/FunctionalTestingFramework/Config/Dom.php +++ b/src/Magento/FunctionalTestingFramework/Config/Dom.php @@ -70,7 +70,7 @@ class Dom * The path to ID attribute name should not include any attribute notations or modifiers -- only node names * * @param string $xml - * @param array $idAttributes + * @param array $idAttributes * @param string $typeAttributeName * @param string $schemaFile * @param string $errorFormat @@ -93,8 +93,8 @@ public function __construct( /** * Merge $xml into DOM document * - * @param string $xml - * @param string $filename + * @param string $xml + * @param string $filename * @param ExceptionCollector $exceptionCollector * @return void */ @@ -113,7 +113,7 @@ public function merge($xml, $filename = null, $exceptionCollector = null) * 3. Append new node if original document doesn't have the same node * * @param \DOMElement $node - * @param string $parentPath path to parent node + * @param string $parentPath Path to parent node. * @return void */ protected function mergeNode(\DOMElement $node, $parentPath) @@ -137,9 +137,9 @@ protected function mergeNode(\DOMElement $node, $parentPath) * Function to process matching node merges. Broken into shared logic for extending classes. * * @param \DomElement $node - * @param string $parentPath + * @param string $parentPath * @param |DomElement $matchedNode - * @param string $path + * @param string $path * @return void * @SuppressWarnings(PHPMD.CyclomaticComplexity) * @SuppressWarnings(PHPMD.NPathComplexity) @@ -183,7 +183,7 @@ protected function mergeMatchingNode(\DomElement $node, $parentPath, $matchedNod /** * Replace node value. * - * @param string $parentPath + * @param string $parentPath * @param \DOMElement $node * @param \DOMElement $matchedNode * @@ -200,7 +200,7 @@ protected function replaceNodeValue($parentPath, \DOMElement $node, \DOMElement * Check if the node content is text * * @param \DOMElement $node - * @return bool + * @return boolean */ protected function isTextNode($node) { @@ -211,7 +211,7 @@ protected function isTextNode($node) * Merges attributes of the merge node to the base node * * @param \DOMElement $baseNode - * @param \DOMNode $mergeNode + * @param \DOMNode $mergeNode * @return void */ protected function mergeAttributes($baseNode, $mergeNode) @@ -233,7 +233,7 @@ protected function mergeAttributes($baseNode, $mergeNode) * Identify node path based on parent path and node attributes * * @param \DOMElement $node - * @param string $parentPath + * @param string $parentPath * @return string */ protected function getNodePathByParent(\DOMElement $node, $parentPath) @@ -280,8 +280,8 @@ protected function getMatchedNode($nodePath) * Validate dom document * * @param \DOMDocument $dom - * @param string $schemaFileName - * @param string $errorFormat + * @param string $schemaFileName + * @param string $errorFormat * @return array of errors * @throws \Exception */ @@ -322,7 +322,7 @@ public static function validateDomDocument( * Render error message string by replacing placeholders '%field%' with properties of \LibXMLError * * @param \LibXMLError $errorInfo - * @param string $format + * @param string $format * @return string * @throws \InvalidArgumentException */ @@ -373,9 +373,9 @@ protected function initDom($xml) /** * Validate self contents towards to specified schema * - * @param string $schemaFileName absolute path to schema file - * @param array &$errors - * @return bool + * @param string $schemaFileName Absolute path to schema file. + * @param array $errors + * @return boolean */ public function validate($schemaFileName, &$errors = []) { diff --git a/src/Magento/FunctionalTestingFramework/Config/Dom/ArrayNodeConfig.php b/src/Magento/FunctionalTestingFramework/Config/Dom/ArrayNodeConfig.php index 8bb06376a..5f03450dc 100644 --- a/src/Magento/FunctionalTestingFramework/Config/Dom/ArrayNodeConfig.php +++ b/src/Magento/FunctionalTestingFramework/Config/Dom/ArrayNodeConfig.php @@ -41,8 +41,8 @@ class ArrayNodeConfig /** * ArrayNodeConfig constructor. * @param NodePathMatcher $nodePathMatcher - * @param array $assocArrayAttributes - * @param array $numericArrays + * @param array $assocArrayAttributes + * @param array $numericArrays */ public function __construct( NodePathMatcher $nodePathMatcher, @@ -59,7 +59,7 @@ public function __construct( * Whether a node is a numeric array or not * * @param string $nodeXpath - * @return bool + * @return boolean */ public function isNumericArray($nodeXpath) { diff --git a/src/Magento/FunctionalTestingFramework/Config/Dom/NodeMergingConfig.php b/src/Magento/FunctionalTestingFramework/Config/Dom/NodeMergingConfig.php index 8990a43f7..e4231c8ec 100644 --- a/src/Magento/FunctionalTestingFramework/Config/Dom/NodeMergingConfig.php +++ b/src/Magento/FunctionalTestingFramework/Config/Dom/NodeMergingConfig.php @@ -27,7 +27,7 @@ class NodeMergingConfig /** * NodeMergingConfig constructor. * @param NodePathMatcher $nodePathMatcher - * @param array $idAttributes + * @param array $idAttributes */ public function __construct(NodePathMatcher $nodePathMatcher, array $idAttributes) { diff --git a/src/Magento/FunctionalTestingFramework/Config/Dom/NodePathMatcher.php b/src/Magento/FunctionalTestingFramework/Config/Dom/NodePathMatcher.php index 15f2c4160..2857bbea6 100644 --- a/src/Magento/FunctionalTestingFramework/Config/Dom/NodePathMatcher.php +++ b/src/Magento/FunctionalTestingFramework/Config/Dom/NodePathMatcher.php @@ -13,9 +13,9 @@ class NodePathMatcher /** * Whether a subject XPath matches to a given path pattern * - * @param string $pathPattern Example: '/some/static/path' or '/some/regexp/path(/item)+' - * @param string $xpathSubject Example: '/some[@attr="value"]/static/ns:path' - * @return bool + * @param string $pathPattern Example: '/some/static/path' or '/some/regexp/path(/item)+'. + * @param string $xpathSubject Example: '/some[@attr="value"]/static/ns:path'. + * @return boolean */ public function match($pathPattern, $xpathSubject) { diff --git a/src/Magento/FunctionalTestingFramework/Config/MftfApplicationConfig.php b/src/Magento/FunctionalTestingFramework/Config/MftfApplicationConfig.php index 81505067f..76403e1c1 100644 --- a/src/Magento/FunctionalTestingFramework/Config/MftfApplicationConfig.php +++ b/src/Magento/FunctionalTestingFramework/Config/MftfApplicationConfig.php @@ -16,7 +16,7 @@ class MftfApplicationConfig /** * Determines whether the user has specified a force option for generation * - * @var bool + * @var boolean */ private $forceGenerate; @@ -30,14 +30,14 @@ class MftfApplicationConfig /** * Determines whether the user would like to execute mftf in a verbose run. * - * @var bool + * @var boolean */ private $verboseEnabled; /** * Determines whether the user would like to execute mftf in a verbose run. * - * @var bool + * @var boolean */ private $debugEnabled; @@ -51,10 +51,10 @@ class MftfApplicationConfig /** * MftfApplicationConfig constructor. * - * @param bool $forceGenerate - * @param string $phase - * @param bool $verboseEnabled - * @param bool $debugEnabled + * @param boolean $forceGenerate + * @param string $phase + * @param boolean $verboseEnabled + * @param boolean $debugEnabled * @throws TestFrameworkException */ private function __construct( @@ -78,10 +78,10 @@ private function __construct( * Creates an instance of the configuration instance for reference once application has started. This function * returns void and is only run once during the lifetime of the application. * - * @param bool $forceGenerate - * @param string $phase - * @param bool $verboseEnabled - * @param bool $debugEnabled + * @param boolean $forceGenerate + * @param string $phase + * @param boolean $verboseEnabled + * @param boolean $debugEnabled * @return void */ public static function create($forceGenerate, $phase, $verboseEnabled, $debugEnabled) @@ -112,7 +112,7 @@ public static function getConfig() /** * Returns a booelan indiciating whether or not the user has indicated a forced generation. * - * @return bool + * @return boolean */ public function forceGenerateEnabled() { @@ -123,7 +123,7 @@ public function forceGenerateEnabled() * Returns a boolean indicating whether the user has indicated a verbose run, which will cause all applicable * text to print to the console. * - * @return bool + * @return boolean */ public function verboseEnabled() { @@ -134,7 +134,7 @@ public function verboseEnabled() * Returns a boolean indicating whether the user has indicated a debug run, which will lengthy validation * with some extra error messaging to be run * - * @return bool + * @return boolean */ public function debugEnabled() { diff --git a/src/Magento/FunctionalTestingFramework/Config/MftfDom.php b/src/Magento/FunctionalTestingFramework/Config/MftfDom.php index 315db4fb7..2a4c5f8b6 100644 --- a/src/Magento/FunctionalTestingFramework/Config/MftfDom.php +++ b/src/Magento/FunctionalTestingFramework/Config/MftfDom.php @@ -21,13 +21,13 @@ class MftfDom extends \Magento\FunctionalTestingFramework\Config\Dom { /** * MftfDom constructor. - * @param string $xml - * @param string $filename + * @param string $xml + * @param string $filename * @param ExceptionCollector $exceptionCollector - * @param array $idAttributes - * @param string $typeAttributeName - * @param string $schemaFile - * @param string $errorFormat + * @param array $idAttributes + * @param string $typeAttributeName + * @param string $schemaFile + * @param string $errorFormat */ public function __construct( $xml, @@ -49,8 +49,8 @@ public function __construct( /** * Redirects any merges into the init method for appending xml filename * - * @param string $xml - * @param string|null $filename + * @param string $xml + * @param string|null $filename * @param ExceptionCollector $exceptionCollector * @return void */ diff --git a/src/Magento/FunctionalTestingFramework/Config/Reader.php b/src/Magento/FunctionalTestingFramework/Config/Reader.php index 9ceebb2f1..0c996f14c 100644 --- a/src/Magento/FunctionalTestingFramework/Config/Reader.php +++ b/src/Magento/FunctionalTestingFramework/Config/Reader.php @@ -24,14 +24,14 @@ class Reader extends \Magento\FunctionalTestingFramework\Config\Reader\Filesyste /** * Reader constructor. - * @param FileResolverInterface $fileResolver - * @param ConverterInterface $converter - * @param SchemaLocatorInterface $schemaLocator + * @param FileResolverInterface $fileResolver + * @param ConverterInterface $converter + * @param SchemaLocatorInterface $schemaLocator * @param ValidationStateInterface $validationState - * @param string $fileName - * @param array $idAttributes - * @param string $domDocumentClass - * @param string $defaultScope + * @param string $fileName + * @param array $idAttributes + * @param string $domDocumentClass + * @param string $defaultScope */ public function __construct( FileResolverInterface $fileResolver, @@ -40,7 +40,7 @@ public function __construct( ValidationStateInterface $validationState, $fileName = 'scenario.xml', $idAttributes = [], - $domDocumentClass = 'Magento\FunctionalTestingFramework\Config\Dom', + $domDocumentClass = Magento\FunctionalTestingFramework\Config\Dom::class, $defaultScope = 'etc' ) { $this->fileResolver = $fileResolver; diff --git a/src/Magento/FunctionalTestingFramework/Config/Reader/Filesystem.php b/src/Magento/FunctionalTestingFramework/Config/Reader/Filesystem.php index c241e9c62..4c3aa581b 100644 --- a/src/Magento/FunctionalTestingFramework/Config/Reader/Filesystem.php +++ b/src/Magento/FunctionalTestingFramework/Config/Reader/Filesystem.php @@ -86,14 +86,14 @@ class Filesystem implements \Magento\FunctionalTestingFramework\Config\ReaderInt /** * Constructor * - * @param \Magento\FunctionalTestingFramework\Config\FileResolverInterface $fileResolver - * @param \Magento\FunctionalTestingFramework\Config\ConverterInterface $converter - * @param \Magento\FunctionalTestingFramework\Config\SchemaLocatorInterface $schemaLocator + * @param \Magento\FunctionalTestingFramework\Config\FileResolverInterface $fileResolver + * @param \Magento\FunctionalTestingFramework\Config\ConverterInterface $converter + * @param \Magento\FunctionalTestingFramework\Config\SchemaLocatorInterface $schemaLocator * @param \Magento\FunctionalTestingFramework\Config\ValidationStateInterface $validationState - * @param string $fileName - * @param array $idAttributes - * @param string $domDocumentClass - * @param string $defaultScope + * @param string $fileName + * @param array $idAttributes + * @param string $domDocumentClass + * @param string $defaultScope */ public function __construct( \Magento\FunctionalTestingFramework\Config\FileResolverInterface $fileResolver, @@ -202,7 +202,7 @@ protected function createConfigMerger($mergerClass, $initialContents) * * @param string $content * @param string $fileName - * @return bool + * @return boolean */ protected function verifyFileEmpty($content, $fileName) { diff --git a/src/Magento/FunctionalTestingFramework/Config/Reader/MftfFilesystem.php b/src/Magento/FunctionalTestingFramework/Config/Reader/MftfFilesystem.php index e09e40290..173792d7c 100644 --- a/src/Magento/FunctionalTestingFramework/Config/Reader/MftfFilesystem.php +++ b/src/Magento/FunctionalTestingFramework/Config/Reader/MftfFilesystem.php @@ -60,9 +60,9 @@ public function readFiles($fileList) /** * Return newly created instance of a config merger * - * @param string $mergerClass - * @param string $initialContents - * @param string $filename + * @param string $mergerClass + * @param string $initialContents + * @param string $filename * @param ExceptionCollector $exceptionCollector * @return \Magento\FunctionalTestingFramework\Config\Dom * @throws \UnexpectedValueException diff --git a/src/Magento/FunctionalTestingFramework/Config/ReplacerInterface.php b/src/Magento/FunctionalTestingFramework/Config/ReplacerInterface.php index 6652f9bc8..ce77a6c42 100644 --- a/src/Magento/FunctionalTestingFramework/Config/ReplacerInterface.php +++ b/src/Magento/FunctionalTestingFramework/Config/ReplacerInterface.php @@ -14,7 +14,7 @@ interface ReplacerInterface /** * Apply specified node in 'replace' attribute instead of original. * - * @param array &$output + * @param array $output * @return array */ public function apply(array &$output); diff --git a/src/Magento/FunctionalTestingFramework/Config/SchemaLocator.php b/src/Magento/FunctionalTestingFramework/Config/SchemaLocator.php index fe88e39e6..a92f536a3 100644 --- a/src/Magento/FunctionalTestingFramework/Config/SchemaLocator.php +++ b/src/Magento/FunctionalTestingFramework/Config/SchemaLocator.php @@ -28,7 +28,7 @@ class SchemaLocator implements \Magento\FunctionalTestingFramework\Config\Schema /** * Class constructor * - * @param string $schemaPath + * @param string $schemaPath * @param string|null $perFileSchema */ public function __construct($schemaPath, $perFileSchema = null) diff --git a/src/Magento/FunctionalTestingFramework/Console/BuildProjectCommand.php b/src/Magento/FunctionalTestingFramework/Console/BuildProjectCommand.php index 17edf557c..c5b48ba83 100644 --- a/src/Magento/FunctionalTestingFramework/Console/BuildProjectCommand.php +++ b/src/Magento/FunctionalTestingFramework/Console/BuildProjectCommand.php @@ -1,5 +1,4 @@ <?php -// @codingStandardsIgnoreFile /** * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. @@ -39,7 +38,12 @@ protected function configure() { $this->setName('build:project') ->setDescription('Generate configuration files for the project. Build the Codeception project.') - ->addOption("upgrade", 'u', InputOption::VALUE_NONE, 'upgrade existing MFTF tests according to last major release requiements'); + ->addOption( + "upgrade", + 'u', + InputOption::VALUE_NONE, + 'upgrade existing MFTF tests according to last major release requiements' + ); $this->envProcessor = new EnvProcessor(TESTS_BP . DIRECTORY_SEPARATOR . '.env'); $env = $this->envProcessor->getEnv(); foreach ($env as $key => $value) { @@ -50,7 +54,7 @@ protected function configure() /** * Executes the current command. * - * @param InputInterface $input + * @param InputInterface $input * @param OutputInterface $output * @return void * @throws \Symfony\Component\Console\Exception\LogicException @@ -77,8 +81,6 @@ protected function execute(InputInterface $input, OutputInterface $output) $commandInput = new ArrayInput($commandInput); $setupEnvCommand->run($commandInput, $output); - - // TODO can we just import the codecept symfony command? $codeceptBuildCommand = realpath(PROJECT_ROOT . '/vendor/bin/codecept') . ' build'; $process = new Process($codeceptBuildCommand); @@ -128,7 +130,7 @@ private function generateConfigFiles(OutputInterface $output) $output->writeln("codeception.yml applied to " . TESTS_BP . DIRECTORY_SEPARATOR . 'codeception.yml'); } - // copy the functional suite yml, this will only copy if there are differences between the template the destination + // copy the functional suite yml, will only copy if there are differences between the template the destination $fileSystem->copy( realpath(FW_BP . '/etc/config/functional.suite.dist.yml'), TESTS_BP . DIRECTORY_SEPARATOR . 'tests' . DIRECTORY_SEPARATOR . 'functional.suite.yml' diff --git a/src/Magento/FunctionalTestingFramework/Console/CleanProjectCommand.php b/src/Magento/FunctionalTestingFramework/Console/CleanProjectCommand.php index 84f85fe28..b2b2c01f3 100644 --- a/src/Magento/FunctionalTestingFramework/Console/CleanProjectCommand.php +++ b/src/Magento/FunctionalTestingFramework/Console/CleanProjectCommand.php @@ -1,5 +1,4 @@ <?php -// @codingStandardsIgnoreFile /** * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. @@ -18,10 +17,14 @@ class CleanProjectCommand extends Command { const CONFIGURATION_FILES = [ - TESTS_BP . DIRECTORY_SEPARATOR . 'codeception.yml', // codeception.yml file for top level config - TESTS_BP . DIRECTORY_SEPARATOR . 'tests' . DIRECTORY_SEPARATOR . 'functional.suite.yml', // functional.suite.yml for test execution config - FW_BP . '/src/Magento/FunctionalTestingFramework/_generated', // Acceptance Tester Actions generated by codeception - FW_BP . '/src/Magento/FunctionalTestingFramework/AcceptanceTester.php' // AcceptanceTester Class generated by codeception + // codeception.yml file for top level config + TESTS_BP . DIRECTORY_SEPARATOR . 'codeception.yml', + // functional.suite.yml for test execution config + TESTS_BP . DIRECTORY_SEPARATOR . 'tests' . DIRECTORY_SEPARATOR . 'functional.suite.yml', + // Acceptance Tester Actions generated by codeception + FW_BP . '/src/Magento/FunctionalTestingFramework/_generated', + // AcceptanceTester Class generated by codeception + FW_BP . '/src/Magento/FunctionalTestingFramework/AcceptanceTester.php' ]; const GENERATED_FILES = [ @@ -36,14 +39,16 @@ class CleanProjectCommand extends Command protected function configure() { $this->setName('reset') - ->setDescription('This command will clean any configuration files from the environment (not including .env), as well as any generated artifacts.') + ->setDescription( + 'This command will clean any configuration files from the environment (not including .env), as well as generated artifacts.' // phpcs:ignore + ) ->addOption('hard', null, InputOption::VALUE_NONE, "parameter to force reset of configuration files."); } /** * Executes the current command. * - * @param InputInterface $input + * @param InputInterface $input * @param OutputInterface $output * @return void * @throws \Symfony\Component\Console\Exception\LogicException diff --git a/src/Magento/FunctionalTestingFramework/Console/GenerateSuiteCommand.php b/src/Magento/FunctionalTestingFramework/Console/GenerateSuiteCommand.php index 7299fef12..ca9be1fac 100644 --- a/src/Magento/FunctionalTestingFramework/Console/GenerateSuiteCommand.php +++ b/src/Magento/FunctionalTestingFramework/Console/GenerateSuiteCommand.php @@ -1,5 +1,4 @@ <?php -// @codingStandardsIgnoreFile /** * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. @@ -35,9 +34,9 @@ protected function configure() /** * Executes the current command. * - * @param InputInterface $input + * @param InputInterface $input * @param OutputInterface $output - * @return int|null|void + * @return integer|null|void * @throws \Exception */ protected function execute(InputInterface $input, OutputInterface $output) diff --git a/src/Magento/FunctionalTestingFramework/Console/GenerateTestsCommand.php b/src/Magento/FunctionalTestingFramework/Console/GenerateTestsCommand.php index 80b4431da..845fb5151 100644 --- a/src/Magento/FunctionalTestingFramework/Console/GenerateTestsCommand.php +++ b/src/Magento/FunctionalTestingFramework/Console/GenerateTestsCommand.php @@ -1,5 +1,4 @@ <?php -// @codingStandardsIgnoreFile /** * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. @@ -32,18 +31,34 @@ protected function configure() { $this->setName('generate:tests') ->setDescription('This command generates all test files and suites based on xml declarations') - ->addArgument('name', InputArgument::OPTIONAL | InputArgument::IS_ARRAY, 'name(s) of specific tests to generate') - ->addOption("config", 'c', InputOption::VALUE_REQUIRED, 'default, singleRun, or parallel', 'default') - ->addOption("force", 'f',InputOption::VALUE_NONE, 'force generation of tests regardless of Magento Instance Configuration') - ->addOption('time', 'i', InputOption::VALUE_REQUIRED, 'Used in combination with a parallel configuration, determines desired group size (in minutes)', 10) - ->addOption('tests', 't', InputOption::VALUE_REQUIRED, 'A parameter accepting a JSON string used to determine the test configuration') - ->addOption('debug', 'd', InputOption::VALUE_NONE, 'run extra validation when generating tests'); + ->addArgument( + 'name', + InputArgument::OPTIONAL | InputArgument::IS_ARRAY, + 'name(s) of specific tests to generate' + )->addOption("config", 'c', InputOption::VALUE_REQUIRED, 'default, singleRun, or parallel', 'default') + ->addOption( + "force", + 'f', + InputOption::VALUE_NONE, + 'force generation of tests regardless of Magento Instance Configuration' + )->addOption( + 'time', + 'i', + InputOption::VALUE_REQUIRED, + 'Used in combination with a parallel configuration, determines desired group size (in minutes)', + 10 + )->addOption( + 'tests', + 't', + InputOption::VALUE_REQUIRED, + 'A parameter accepting a JSON string used to determine the test configuration' + )->addOption('debug', 'd', InputOption::VALUE_NONE, 'run extra validation when generating tests'); } /** * Executes the current command. * - * @param InputInterface $input + * @param InputInterface $input * @param OutputInterface $output * @return void * @throws TestFrameworkException @@ -87,17 +102,17 @@ protected function execute(InputInterface $input, OutputInterface $output) $testManifest->generate(); - $output->writeln("Generate Tests Command Run"); + $output->writeln("Generate Tests Command Run"); } /** * Function which builds up a configuration including test and suites for consumption of Magento generation methods. * - * @param string $json - * @param array $tests - * @param bool $force - * @param bool $debug - * @param bool $verbose + * @param string $json + * @param array $tests + * @param boolean $force + * @param boolean $debug + * @param boolean $verbose * @return array * @throws \Magento\FunctionalTestingFramework\Exceptions\TestReferenceException * @throws \Magento\FunctionalTestingFramework\Exceptions\XmlException @@ -137,7 +152,8 @@ private function createTestConfiguration($json, array $tests, bool $force, bool * passed in by the user. The result is a testConfiguration array. * * @param string $json - * @param array $testConfiguration + * @param array $testConfiguration + * @throws TestFrameworkException * @return array */ private function parseTestsConfigJson($json, array $testConfiguration) @@ -149,7 +165,8 @@ private function parseTestsConfigJson($json, array $testConfiguration) $jsonTestConfiguration = []; $testConfigArray = json_decode($json, true); - $jsonTestConfiguration['tests'] = $testConfigArray['tests'] ?? null;; + $jsonTestConfiguration['tests'] = $testConfigArray['tests'] ?? null; + ; $jsonTestConfiguration['suites'] = $testConfigArray['suites'] ?? null; return $jsonTestConfiguration; } diff --git a/src/Magento/FunctionalTestingFramework/Console/RunTestCommand.php b/src/Magento/FunctionalTestingFramework/Console/RunTestCommand.php index 7352985a3..4caf4287a 100644 --- a/src/Magento/FunctionalTestingFramework/Console/RunTestCommand.php +++ b/src/Magento/FunctionalTestingFramework/Console/RunTestCommand.php @@ -1,5 +1,4 @@ <?php -// @codingStandardsIgnoreFile /** * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. @@ -27,16 +26,19 @@ protected function configure() { $this->setName("run:test") ->setDescription("generation and execution of test(s) defined in xml") - ->addArgument('name', InputArgument::REQUIRED | InputArgument::IS_ARRAY, "name of tests to generate and execute") - ->addOption('skip-generate', 'k', InputOption::VALUE_NONE, "skip generation and execute existing test"); + ->addArgument( + 'name', + InputArgument::REQUIRED | InputArgument::IS_ARRAY, + "name of tests to generate and execute" + )->addOption('skip-generate', 'k', InputOption::VALUE_NONE, "skip generation and execute existing test"); } /** * Executes the current command. * - * @param InputInterface $input + * @param InputInterface $input * @param OutputInterface $output - * @return int|null|void + * @return integer|null|void * @throws \Exception * * @SuppressWarnings(PHPMD.UnusedLocalVariable) diff --git a/src/Magento/FunctionalTestingFramework/Console/RunTestGroupCommand.php b/src/Magento/FunctionalTestingFramework/Console/RunTestGroupCommand.php index c93ccf5d3..06e296b58 100644 --- a/src/Magento/FunctionalTestingFramework/Console/RunTestGroupCommand.php +++ b/src/Magento/FunctionalTestingFramework/Console/RunTestGroupCommand.php @@ -1,5 +1,4 @@ <?php -// @codingStandardsIgnoreFile /** * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. @@ -29,8 +28,12 @@ protected function configure() { $this->setName('run:group') ->setDescription('Execute a set of tests referenced via group annotations') - ->addOption('skip-generate', 'k', InputOption::VALUE_NONE, "only execute a group of tests without generating from source xml") - ->addArgument( + ->addOption( + 'skip-generate', + 'k', + InputOption::VALUE_NONE, + "only execute a group of tests without generating from source xml" + )->addArgument( 'groups', InputArgument::IS_ARRAY | InputArgument::REQUIRED, 'group names to be executed via codeception' @@ -40,9 +43,9 @@ protected function configure() /** * Executes the current command. * - * @param InputInterface $input + * @param InputInterface $input * @param OutputInterface $output - * @return int|null|void + * @return integer|null|void * @throws \Exception * * @SuppressWarnings(PHPMD.UnusedLocalVariable) diff --git a/src/Magento/FunctionalTestingFramework/Console/SetupEnvCommand.php b/src/Magento/FunctionalTestingFramework/Console/SetupEnvCommand.php index 5b57c43d7..4d1ce0334 100644 --- a/src/Magento/FunctionalTestingFramework/Console/SetupEnvCommand.php +++ b/src/Magento/FunctionalTestingFramework/Console/SetupEnvCommand.php @@ -1,5 +1,4 @@ <?php -// @codingStandardsIgnoreFile /** * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. @@ -43,7 +42,7 @@ protected function configure() /** * Executes the current command. * - * @param InputInterface $input + * @param InputInterface $input * @param OutputInterface $output * @return void * @throws \Symfony\Component\Console\Exception\LogicException diff --git a/src/Magento/FunctionalTestingFramework/Data/Argument/Interpreter/ArrayType.php b/src/Magento/FunctionalTestingFramework/Data/Argument/Interpreter/ArrayType.php index 056a4f9d2..566c09912 100644 --- a/src/Magento/FunctionalTestingFramework/Data/Argument/Interpreter/ArrayType.php +++ b/src/Magento/FunctionalTestingFramework/Data/Argument/Interpreter/ArrayType.php @@ -79,10 +79,10 @@ function ($firstItemKey, $secondItemKey) use ($indexedItems) { /** * Compare sortOrder of item * - * @param string|int $firstItemKey - * @param string|int $secondItemKey - * @param array $indexedItems - * @return int + * @param string|integer $firstItemKey + * @param string|integer $secondItemKey + * @param array $indexedItems + * @return integer */ private function compareItems($firstItemKey, $secondItemKey, $indexedItems) { @@ -110,7 +110,7 @@ private function compareItems($firstItemKey, $secondItemKey, $indexedItems) * Determine if a sort order exists for any of the items. * * @param array $items - * @return bool + * @return boolean */ private function isSortOrderDefined($items) { diff --git a/src/Magento/FunctionalTestingFramework/Data/Argument/Interpreter/Boolean.php b/src/Magento/FunctionalTestingFramework/Data/Argument/Interpreter/Boolean.php index f545c2aa4..8e4e5a899 100644 --- a/src/Magento/FunctionalTestingFramework/Data/Argument/Interpreter/Boolean.php +++ b/src/Magento/FunctionalTestingFramework/Data/Argument/Interpreter/Boolean.php @@ -31,7 +31,7 @@ public function __construct(BooleanUtils $booleanUtils) /** * {@inheritdoc} - * @return bool + * @return boolean * @throws \InvalidArgumentException */ public function evaluate(array $data) diff --git a/src/Magento/FunctionalTestingFramework/Data/Argument/Interpreter/Composite.php b/src/Magento/FunctionalTestingFramework/Data/Argument/Interpreter/Composite.php index 2d3ea0e0e..8c5981a78 100644 --- a/src/Magento/FunctionalTestingFramework/Data/Argument/Interpreter/Composite.php +++ b/src/Magento/FunctionalTestingFramework/Data/Argument/Interpreter/Composite.php @@ -28,7 +28,7 @@ class Composite implements InterpreterInterface /** * Composite constructor. - * @param array $interpreters + * @param array $interpreters * @param string $discriminator * @throws \InvalidArgumentException */ @@ -47,6 +47,7 @@ public function __construct(array $interpreters, $discriminator) /** * {@inheritdoc} + * @return mixed * @throws \InvalidArgumentException */ public function evaluate(array $data) @@ -65,7 +66,7 @@ public function evaluate(array $data) /** * Register interpreter instance under a given unique name * - * @param string $name + * @param string $name * @param InterpreterInterface $instance * @return void * @throws \InvalidArgumentException diff --git a/src/Magento/FunctionalTestingFramework/Data/Argument/Interpreter/Number.php b/src/Magento/FunctionalTestingFramework/Data/Argument/Interpreter/Number.php index 3583896c4..3742a67ad 100644 --- a/src/Magento/FunctionalTestingFramework/Data/Argument/Interpreter/Number.php +++ b/src/Magento/FunctionalTestingFramework/Data/Argument/Interpreter/Number.php @@ -14,7 +14,7 @@ class Number implements InterpreterInterface { /** * {@inheritdoc} - * @return string|int|float + * @return string|integer|float * @throws \InvalidArgumentException */ public function evaluate(array $data) diff --git a/src/Magento/FunctionalTestingFramework/Data/Argument/InterpreterInterface.php b/src/Magento/FunctionalTestingFramework/Data/Argument/InterpreterInterface.php index 6ff11b082..6aa835e3e 100644 --- a/src/Magento/FunctionalTestingFramework/Data/Argument/InterpreterInterface.php +++ b/src/Magento/FunctionalTestingFramework/Data/Argument/InterpreterInterface.php @@ -8,7 +8,6 @@ /** * Interface that encapsulates complexity of expression computation */ -// @codingStandardsIgnoreFile interface InterpreterInterface { /** diff --git a/src/Magento/FunctionalTestingFramework/Data/Argument/InterpreterInterface/Proxy.php b/src/Magento/FunctionalTestingFramework/Data/Argument/InterpreterInterface/Proxy.php index 1741952a6..543682b4f 100644 --- a/src/Magento/FunctionalTestingFramework/Data/Argument/InterpreterInterface/Proxy.php +++ b/src/Magento/FunctionalTestingFramework/Data/Argument/InterpreterInterface/Proxy.php @@ -34,7 +34,7 @@ class Proxy implements \Magento\FunctionalTestingFramework\Data\Argument\Interpr /** * Instance shareability flag * - * @var bool + * @var boolean */ protected $isShared = null; @@ -42,8 +42,8 @@ class Proxy implements \Magento\FunctionalTestingFramework\Data\Argument\Interpr * Proxy constructor * * @param \Magento\FunctionalTestingFramework\ObjectManagerInterface $objectManager - * @param string $instanceName - * @param bool $shared + * @param string $instanceName + * @param boolean $shared */ public function __construct( \Magento\FunctionalTestingFramework\ObjectManagerInterface $objectManager, @@ -100,6 +100,7 @@ protected function getSubject() /** * {@inheritdoc} + * @return mixed */ public function evaluate(array $data) { diff --git a/src/Magento/FunctionalTestingFramework/DataGenerator/Config/Dom.php b/src/Magento/FunctionalTestingFramework/DataGenerator/Config/Dom.php index 683b6fdda..1da3e9c0e 100644 --- a/src/Magento/FunctionalTestingFramework/DataGenerator/Config/Dom.php +++ b/src/Magento/FunctionalTestingFramework/DataGenerator/Config/Dom.php @@ -27,13 +27,13 @@ class Dom extends \Magento\FunctionalTestingFramework\Config\MftfDom /** * Entity Dom constructor. - * @param string $xml - * @param string $filename + * @param string $xml + * @param string $filename * @param ExceptionCollector $exceptionCollector - * @param array $idAttributes - * @param string $typeAttributeName - * @param string $schemaFile - * @param string $errorFormat + * @param array $idAttributes + * @param string $typeAttributeName + * @param string $schemaFile + * @param string $errorFormat */ public function __construct( $xml, @@ -59,7 +59,7 @@ public function __construct( /** * Takes a dom element from xml and appends the filename based on location * - * @param string $xml + * @param string $xml * @param string|null $filename * @return \DOMDocument */ diff --git a/src/Magento/FunctionalTestingFramework/DataGenerator/Config/OperationDom.php b/src/Magento/FunctionalTestingFramework/DataGenerator/Config/OperationDom.php index 036bca53c..f4617c61e 100644 --- a/src/Magento/FunctionalTestingFramework/DataGenerator/Config/OperationDom.php +++ b/src/Magento/FunctionalTestingFramework/DataGenerator/Config/OperationDom.php @@ -27,13 +27,13 @@ class OperationDom extends \Magento\FunctionalTestingFramework\Config\MftfDom /** * Metadata Dom constructor. - * @param string $xml - * @param string $filename + * @param string $xml + * @param string $filename * @param ExceptionCollector $exceptionCollector - * @param array $idAttributes - * @param string $typeAttributeName - * @param string $schemaFile - * @param string $errorFormat + * @param array $idAttributes + * @param string $typeAttributeName + * @param string $schemaFile + * @param string $errorFormat */ public function __construct( $xml, @@ -59,7 +59,7 @@ public function __construct( /** * Takes a dom element from xml and appends the filename based on location * - * @param string $xml + * @param string $xml * @param string|null $filename * @return \DOMDocument */ @@ -85,7 +85,7 @@ public function initDom($xml, $filename = null) /** * Recurse through child elements and validate uniqueKeys * @param \DOMElement $parentNode - * @param string $filename + * @param string $filename * @return void */ public function validateOperationElements(\DOMElement $parentNode, $filename) diff --git a/src/Magento/FunctionalTestingFramework/DataGenerator/Config/Reader/Filesystem.php b/src/Magento/FunctionalTestingFramework/DataGenerator/Config/Reader/Filesystem.php new file mode 100644 index 000000000..4f21fabea --- /dev/null +++ b/src/Magento/FunctionalTestingFramework/DataGenerator/Config/Reader/Filesystem.php @@ -0,0 +1,81 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\FunctionalTestingFramework\DataGenerator\Config\Reader; + +/** + * Filesystem configuration loader. Loads configuration from XML files, split by scopes. + */ +class Filesystem extends \Magento\FunctionalTestingFramework\Config\Reader\Filesystem +{ + /** + * An array of paths which do have a key for merging but instead are value based nodes which can only be appended + * + * @var array + */ + private $mergeablePaths; + + /** + * Constructor + * + * @param \Magento\FunctionalTestingFramework\Config\FileResolverInterface $fileResolver + * @param \Magento\FunctionalTestingFramework\Config\ConverterInterface $converter + * @param \Magento\FunctionalTestingFramework\Config\SchemaLocatorInterface $schemaLocator + * @param \Magento\FunctionalTestingFramework\Config\ValidationStateInterface $validationState + * @param string $fileName + * @param array $idAttributes + * @param array $mergeablePaths + * @param string $domDocumentClass + * @param string $defaultScope + */ + public function __construct( + \Magento\FunctionalTestingFramework\Config\FileResolverInterface $fileResolver, + \Magento\FunctionalTestingFramework\Config\ConverterInterface $converter, + \Magento\FunctionalTestingFramework\Config\SchemaLocatorInterface $schemaLocator, + \Magento\FunctionalTestingFramework\Config\ValidationStateInterface $validationState, + $fileName, + $idAttributes = [], + $mergeablePaths = [], + $domDocumentClass = \Magento\FunctionalTestingFramework\Config\Dom::class, + $defaultScope = 'global' + ) { + $this->fileResolver = $fileResolver; + $this->converter = $converter; + $this->fileName = $fileName; + $this->idAttributes = array_replace($this->idAttributes, $idAttributes); + $this->mergeablePaths = $mergeablePaths; + $this->validationState = $validationState; + $this->schemaFile = $schemaLocator->getSchema(); + $this->perFileSchema = $schemaLocator->getPerFileSchema() && $validationState->isValidationRequired() + ? $schemaLocator->getPerFileSchema() : null; + $this->domDocumentClass = $domDocumentClass; + $this->defaultScope = $defaultScope; + } + + /** + * Return newly created instance of a config merger. Overridden to include new arg in mergerClass. + * + * @param string $mergerClass + * @param string $initialContents + * @return \Magento\FunctionalTestingFramework\Config\Dom + * @throws \UnexpectedValueException + */ + protected function createConfigMerger($mergerClass, $initialContents) + { + $result = new $mergerClass( + $initialContents, + $this->idAttributes, + $this->mergeablePaths, + null, + $this->perFileSchema + ); + if (!$result instanceof \Magento\FunctionalTestingFramework\Config\Dom) { + throw new \UnexpectedValueException( + "Instance of the DOM config merger is expected, got {$mergerClass} instead." + ); + } + return $result; + } +} diff --git a/src/Magento/FunctionalTestingFramework/DataGenerator/Handlers/DataObjectHandler.php b/src/Magento/FunctionalTestingFramework/DataGenerator/Handlers/DataObjectHandler.php index 9ab14e5ff..7305fdc37 100644 --- a/src/Magento/FunctionalTestingFramework/DataGenerator/Handlers/DataObjectHandler.php +++ b/src/Magento/FunctionalTestingFramework/DataGenerator/Handlers/DataObjectHandler.php @@ -114,7 +114,7 @@ public function getAllObjects() /** * Convert the parser output into a collection of EntityDataObjects * - * @param string[] $parserOutput primitive array output from the Magento parser + * @param string[] $parserOutput Primitive array output from the Magento parser. * @return EntityDataObject[] * @throws XmlException */ @@ -179,8 +179,8 @@ private function processParserOutput($parserOutput) /** * Takes an array of items and a top level entity data array and merges in elements from parsed entity definitions. * - * @param array $arrayItems - * @param array $data + * @param array $arrayItems + * @param array $data * @param string $key * @return array */ diff --git a/src/Magento/FunctionalTestingFramework/DataGenerator/Objects/EntityDataObject.php b/src/Magento/FunctionalTestingFramework/DataGenerator/Objects/EntityDataObject.php index 736f76e01..1ba126e5f 100644 --- a/src/Magento/FunctionalTestingFramework/DataGenerator/Objects/EntityDataObject.php +++ b/src/Magento/FunctionalTestingFramework/DataGenerator/Objects/EntityDataObject.php @@ -75,13 +75,13 @@ class EntityDataObject /** * Constructor * - * @param string $name - * @param string $type + * @param string $name + * @param string $type * @param string[] $data * @param string[] $linkedEntities * @param string[] $uniquenessData * @param string[] $vars - * @param string $parentEntity + * @param string $parentEntity */ public function __construct($name, $type, $data, $linkedEntities, $uniquenessData, $vars = [], $parentEntity = null) { @@ -130,8 +130,8 @@ public function getAllData() /** * Get a piece of data by name and the desired uniqueness format. * - * @param string $name - * @param int $uniquenessFormat + * @param string $name + * @param integer $uniquenessFormat * @return string|null * @throws TestFrameworkException */ @@ -326,8 +326,8 @@ public function getUniquenessData() /** * Validate if input value is a valid unique data format. * - * @param int $uniDataFormat - * @return bool + * @param integer $uniDataFormat + * @return boolean */ private function isValidUniqueDataFormat($uniDataFormat) { diff --git a/src/Magento/FunctionalTestingFramework/DataGenerator/Objects/OperationDefinitionObject.php b/src/Magento/FunctionalTestingFramework/DataGenerator/Objects/OperationDefinitionObject.php index abedf14e3..d6237a437 100644 --- a/src/Magento/FunctionalTestingFramework/DataGenerator/Objects/OperationDefinitionObject.php +++ b/src/Magento/FunctionalTestingFramework/DataGenerator/Objects/OperationDefinitionObject.php @@ -106,25 +106,25 @@ class OperationDefinitionObject /** * Determines if operation should remove backend_name from URL. - * @var bool + * @var boolean */ private $removeBackend; /** * OperationDefinitionObject constructor. - * @param string $name - * @param string $operation - * @param string $dataType - * @param string $apiMethod - * @param string $apiUri - * @param string $auth - * @param array $headers - * @param array $params - * @param array $metaData - * @param string $contentType - * @param bool $removeBackend - * @param string $successRegex - * @param string $returnRegex + * @param string $name + * @param string $operation + * @param string $dataType + * @param string $apiMethod + * @param string $apiUri + * @param string $auth + * @param array $headers + * @param array $params + * @param array $metaData + * @param string $contentType + * @param boolean $removeBackend + * @param string $successRegex + * @param string $returnRegex * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ public function __construct( @@ -237,7 +237,7 @@ public function getHeaders() /** * Getter for removeBackend * - * @return bool + * @return boolean */ public function removeUrlBackend() { diff --git a/src/Magento/FunctionalTestingFramework/DataGenerator/Objects/OperationElement.php b/src/Magento/FunctionalTestingFramework/DataGenerator/Objects/OperationElement.php index 5980e2f0a..7338e52f3 100644 --- a/src/Magento/FunctionalTestingFramework/DataGenerator/Objects/OperationElement.php +++ b/src/Magento/FunctionalTestingFramework/DataGenerator/Objects/OperationElement.php @@ -45,17 +45,17 @@ class OperationElement /** * Required attribute, used to determine if values need to be cast before insertion. - * @var bool + * @var boolean */ private $required; /** * OperationElement constructor. - * @param string $key - * @param string $value - * @param string $type - * @param bool $required - * @param array $nestedElements + * @param string $key + * @param string $value + * @param string $type + * @param boolean $required + * @param array $nestedElements * @param null|array $nestedMetadata */ public function __construct($key, $value, $type, $required, $nestedElements = [], $nestedMetadata = null) @@ -105,7 +105,7 @@ public function getType() /** * Accessor for required attribute * - * @return bool + * @return boolean */ public function isRequired() { diff --git a/src/Magento/FunctionalTestingFramework/DataGenerator/Persist/Curl/AdminExecutor.php b/src/Magento/FunctionalTestingFramework/DataGenerator/Persist/Curl/AdminExecutor.php index bec0e8fd3..cff7c04fb 100644 --- a/src/Magento/FunctionalTestingFramework/DataGenerator/Persist/Curl/AdminExecutor.php +++ b/src/Magento/FunctionalTestingFramework/DataGenerator/Persist/Curl/AdminExecutor.php @@ -38,7 +38,7 @@ class AdminExecutor extends AbstractExecutor implements CurlInterface /** * Should executor remove backend_name from api url - * @var bool + * @var boolean */ private $removeBackend; @@ -51,7 +51,7 @@ class AdminExecutor extends AbstractExecutor implements CurlInterface /** * Constructor. - * @param bool $removeBackend + * @param boolean $removeBackend * * @constructor * @throws TestFrameworkException @@ -110,9 +110,9 @@ private function setFormKey() * Send request to the remote server. * * @param string $url - * @param array $data + * @param array $data * @param string $method - * @param array $headers + * @param array $headers * @return void * @throws TestFrameworkException */ @@ -168,8 +168,8 @@ public function read($successRegex = null, $returnRegex = null) /** * Add additional option to cURL. * - * @param int $option the CURLOPT_* constants - * @param int|string|bool|array $value + * @param integer $option CURLOPT_* constants. + * @param integer|string|boolean|array $value * @return void */ public function addOption($option, $value) diff --git a/src/Magento/FunctionalTestingFramework/DataGenerator/Persist/Curl/FrontendExecutor.php b/src/Magento/FunctionalTestingFramework/DataGenerator/Persist/Curl/FrontendExecutor.php index 4725c2c1e..aa1706ef3 100644 --- a/src/Magento/FunctionalTestingFramework/DataGenerator/Persist/Curl/FrontendExecutor.php +++ b/src/Magento/FunctionalTestingFramework/DataGenerator/Persist/Curl/FrontendExecutor.php @@ -132,9 +132,9 @@ protected function setCookies() * Send request to the remote server. * * @param string $url - * @param array $data + * @param array $data * @param string $method - * @param array $headers + * @param array $headers * @return void * @throws TestFrameworkException */ @@ -191,8 +191,8 @@ public function read($successRegex = null, $returnRegex = null) /** * Add additional option to cURL. * - * @param int $option the CURLOPT_* constants - * @param int|string|bool|array $value + * @param integer $option CURLOPT_* constants. + * @param integer|string|boolean|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 4e07c0bf5..16fef75f2 100644 --- a/src/Magento/FunctionalTestingFramework/DataGenerator/Persist/Curl/WebapiExecutor.php +++ b/src/Magento/FunctionalTestingFramework/DataGenerator/Persist/Curl/WebapiExecutor.php @@ -93,9 +93,9 @@ protected function authorize() * Send request to the remote server. * * @param string $url - * @param array $data + * @param array $data * @param string $method - * @param array $headers + * @param array $headers * @return void * @throws TestFrameworkException */ @@ -126,8 +126,8 @@ public function read($successRegex = null, $returnRegex = null) /** * Add additional option to cURL. * - * @param int $option the CURLOPT_* constants - * @param int|string|bool|array $value + * @param integer $option CURLOPT_* constants. + * @param integer|string|boolean|array $value * @return void */ public function addOption($option, $value) diff --git a/src/Magento/FunctionalTestingFramework/DataGenerator/Persist/CurlHandler.php b/src/Magento/FunctionalTestingFramework/DataGenerator/Persist/CurlHandler.php index 437ce69d5..ee61d8c12 100644 --- a/src/Magento/FunctionalTestingFramework/DataGenerator/Persist/CurlHandler.php +++ b/src/Magento/FunctionalTestingFramework/DataGenerator/Persist/CurlHandler.php @@ -58,7 +58,7 @@ class CurlHandler /** * If the content type is Json. * - * @var bool + * @var boolean */ private $isJson; @@ -77,9 +77,9 @@ class CurlHandler /** * ApiSubObject constructor. * - * @param string $operation + * @param string $operation * @param EntityDataObject $entityObject - * @param string $storeCode + * @param string $storeCode */ public function __construct($operation, $entityObject, $storeCode = null) { @@ -179,7 +179,7 @@ public function getRequestDataArray() /** * If content type of a request is Json. * - * @return bool + * @return boolean */ public function isContentTypeJson() { @@ -190,7 +190,7 @@ public function isContentTypeJson() * Resolve rul reference from entity objects. * * @param string $urlIn - * @param array $entityObjects + * @param array $entityObjects * @return string */ private function resolveUrlReference($urlIn, $entityObjects) diff --git a/src/Magento/FunctionalTestingFramework/DataGenerator/Persist/DataPersistenceHandler.php b/src/Magento/FunctionalTestingFramework/DataGenerator/Persist/DataPersistenceHandler.php index e19151980..dcb9d158a 100644 --- a/src/Magento/FunctionalTestingFramework/DataGenerator/Persist/DataPersistenceHandler.php +++ b/src/Magento/FunctionalTestingFramework/DataGenerator/Persist/DataPersistenceHandler.php @@ -46,8 +46,8 @@ class DataPersistenceHandler * DataPersistenceHandler constructor. * * @param EntityDataObject $entityObject - * @param array $dependentObjects - * @param array $customFields + * @param array $dependentObjects + * @param array $customFields */ public function __construct($entityObject, $dependentObjects = [], $customFields = []) { @@ -97,12 +97,11 @@ public function createEntity($storeCode = null) * Function which executes a put request based on specific operation metadata. * * @param string $updateDataName - * @param array $updateDependentObjects + * @param array $updateDependentObjects * @return void * @throws TestFrameworkException * @throws \Exception */ - public function updateEntity($updateDataName, $updateDependentObjects = []) { foreach ($updateDependentObjects as $dependentObject) { @@ -123,11 +122,10 @@ public function updateEntity($updateDataName, $updateDependentObjects = []) * Function which executes a get request on specific operation metadata. * * @param integer|null $index - * @param string $storeCode + * @param string $storeCode * @return void * @throws TestFrameworkException */ - public function getEntity($index = null, $storeCode = null) { if (!empty($storeCode)) { @@ -181,8 +179,8 @@ public function getCreatedDataByName($dataName) * * @param string|array $response * @param integer|null $index - * @param array $requestDataArray - * @param bool $isJson + * @param array $requestDataArray + * @param boolean $isJson * @return void */ private function setCreatedObject($response, $index, $requestDataArray, $isJson) @@ -216,7 +214,7 @@ private function setCreatedObject($response, $index, $requestDataArray, $isJson) /** * Convert an multi-dimensional array to flat array. * - * @param array $arrayIn + * @param array $arrayIn * @param string $rootKey * @return array */ diff --git a/src/Magento/FunctionalTestingFramework/DataGenerator/Persist/OperationDataArrayResolver.php b/src/Magento/FunctionalTestingFramework/DataGenerator/Persist/OperationDataArrayResolver.php index 6f5a1b1d3..a14d384a8 100644 --- a/src/Magento/FunctionalTestingFramework/DataGenerator/Persist/OperationDataArrayResolver.php +++ b/src/Magento/FunctionalTestingFramework/DataGenerator/Persist/OperationDataArrayResolver.php @@ -60,9 +60,9 @@ public function __construct($dependentEntities = null) * structure for the request of the desired entity type. * * @param EntityDataObject $entityObject - * @param array $operationMetadata - * @param string $operation - * @param bool $fromArray + * @param array $operationMetadata + * @param string $operation + * @param boolean $fromArray * @return array * @throws \Exception * @SuppressWarnings(PHPMD.CyclomaticComplexity) @@ -125,7 +125,6 @@ public function resolveOperationDataArray($entityObject, $operationMetadata, $op $operationElementType, $elementData ); - } elseif ($operationElement->isRequired()) { throw new \Exception(sprintf( self::EXCEPTION_REQUIRED_DATA, @@ -172,8 +171,8 @@ public function resolveOperationDataArray($entityObject, $operationMetadata, $op * entities. * * @param EntityDataObject $entityObject - * @param string $operationKey - * @param string $operationElementType + * @param string $operationKey + * @param string $operationElementType * @return array|string * @throws TestFrameworkException */ @@ -198,7 +197,6 @@ private function resolvePrimitiveReference($entityObject, $operationKey, $operat } return $elementDatas; - } $entity = $this->getDependentEntitiesOfType($type)[0]; @@ -234,7 +232,7 @@ private function getDependentEntitiesOfType($type) * the object. * * @param EntityDataObject $entityObject - * @param string $operationElementValue + * @param string $operationElementValue * @return EntityDataObject|null * @throws \Exception */ @@ -256,10 +254,10 @@ private function resolveOperationObjectAndEntityData($entityObject, $operationEl /** * Resolves DataObjects and pre-defined metadata (in other operation.xml file) referenced by the operation * - * @param string $entityName + * @param string $entityName * @param OperationElement $operationElement - * @param string $operation - * @param bool $fromArray + * @param string $operation + * @param boolean $fromArray * @return array * @throws \Exception */ @@ -325,7 +323,7 @@ private static function incrementSequence($entityName) * Get the current sequence number for an entity. * * @param string $entityName - * @return int + * @return integer */ private static function getSequence($entityName) { diff --git a/src/Magento/FunctionalTestingFramework/DataGenerator/Util/OperationElementExtractor.php b/src/Magento/FunctionalTestingFramework/DataGenerator/Util/OperationElementExtractor.php index 6f2e3e447..35d188f5b 100644 --- a/src/Magento/FunctionalTestingFramework/DataGenerator/Util/OperationElementExtractor.php +++ b/src/Magento/FunctionalTestingFramework/DataGenerator/Util/OperationElementExtractor.php @@ -86,7 +86,7 @@ public function extractOperationElement($operationElementArray) /** * Creates and Adds relevant DataElements from data entries defined within dataObject array * - * @param array &$operationElements + * @param array $operationElements * @param array $operationFieldArray * @return void */ @@ -105,7 +105,7 @@ private function extractOperationField(&$operationElements, $operationFieldArray /** * Creates and Adds relevant DataElements from data arrays defined within dataObject array * - * @param array &$operationArrayData + * @param array $operationArrayData * @param array $operationArrayArray * @return void */ diff --git a/src/Magento/FunctionalTestingFramework/Exceptions/TestFrameworkException.php b/src/Magento/FunctionalTestingFramework/Exceptions/TestFrameworkException.php index 3820e25d9..5e9e594c0 100644 --- a/src/Magento/FunctionalTestingFramework/Exceptions/TestFrameworkException.php +++ b/src/Magento/FunctionalTestingFramework/Exceptions/TestFrameworkException.php @@ -16,7 +16,7 @@ class TestFrameworkException extends \Exception /** * TestFrameworkException constructor. * @param string $message - * @param array $context + * @param array $context * @SuppressWarnings(PHPMD.UnusedLocalVariable) */ public function __construct($message, $context = []) diff --git a/src/Magento/FunctionalTestingFramework/Exceptions/TestReferenceException.php b/src/Magento/FunctionalTestingFramework/Exceptions/TestReferenceException.php index df871c75f..0546c4a22 100644 --- a/src/Magento/FunctionalTestingFramework/Exceptions/TestReferenceException.php +++ b/src/Magento/FunctionalTestingFramework/Exceptions/TestReferenceException.php @@ -16,7 +16,7 @@ class TestReferenceException extends \Exception /** * TestReferenceException constructor. * @param string $message - * @param array $context + * @param array $context * @SuppressWarnings(PHPMD.UnusedLocalVariable) */ public function __construct($message, $context = []) diff --git a/src/Magento/FunctionalTestingFramework/Exceptions/XmlException.php b/src/Magento/FunctionalTestingFramework/Exceptions/XmlException.php index ac44e1885..929ffd937 100644 --- a/src/Magento/FunctionalTestingFramework/Exceptions/XmlException.php +++ b/src/Magento/FunctionalTestingFramework/Exceptions/XmlException.php @@ -16,7 +16,7 @@ class XmlException extends \Exception /** * XmlException constructor. * @param string $message - * @param array $context + * @param array $context * @SuppressWarnings(PHPMD.UnusedLocalVariable) */ public function __construct($message, $context = []) diff --git a/src/Magento/FunctionalTestingFramework/Extension/ErrorLogger.php b/src/Magento/FunctionalTestingFramework/Extension/ErrorLogger.php index 822395921..bc29fcd64 100644 --- a/src/Magento/FunctionalTestingFramework/Extension/ErrorLogger.php +++ b/src/Magento/FunctionalTestingFramework/Extension/ErrorLogger.php @@ -42,7 +42,7 @@ private function __construct() /** * Loops through stepEvent for browser log entries * @param \Facebook\WebDriver\Remote\RemoteWebDriver $webDriver - * @param \Codeception\Event\StepEvent $stepEvent + * @param \Codeception\Event\StepEvent $stepEvent * @return void */ public function logErrors($webDriver, $stepEvent) @@ -58,9 +58,9 @@ public function logErrors($webDriver, $stepEvent) /** * Logs errors to console/report. - * @param string $type + * @param string $type * @param \Codeception\Event\StepEvent $stepEvent - * @param array $entry + * @param array $entry * @return void */ private function logError($type, $stepEvent, $entry) diff --git a/src/Magento/FunctionalTestingFramework/Extension/TestContextExtension.php b/src/Magento/FunctionalTestingFramework/Extension/TestContextExtension.php index c03743dc3..b51ebda0f 100644 --- a/src/Magento/FunctionalTestingFramework/Extension/TestContextExtension.php +++ b/src/Magento/FunctionalTestingFramework/Extension/TestContextExtension.php @@ -56,7 +56,7 @@ function () use ($cest, $I) { /** * Extracts hook method from trace, looking specifically for the cest class given. - * @param array $trace + * @param array $trace * @param string $class * @return string */ @@ -79,7 +79,9 @@ public function extractContext($trace, $class) */ public function afterStep(\Codeception\Event\StepEvent $e) { + // @codingStandardsIgnoreStart $webDriver = $this->getModule("\Magento\FunctionalTestingFramework\Module\MagentoWebDriver")->webDriver; + // @codingStandardsIgnoreEnd ErrorLogger::getInstance()->logErrors($webDriver, $e); } } diff --git a/src/Magento/FunctionalTestingFramework/Helper/EntityRESTApiHelper.php b/src/Magento/FunctionalTestingFramework/Helper/EntityRESTApiHelper.php index 5358b4461..521ddc9ee 100644 --- a/src/Magento/FunctionalTestingFramework/Helper/EntityRESTApiHelper.php +++ b/src/Magento/FunctionalTestingFramework/Helper/EntityRESTApiHelper.php @@ -50,7 +50,7 @@ public function __construct($host, $port) * @param string $apiMethod * @param string $requestURI * @param string $jsonBody - * @param array $headers + * @param array $headers * @return \Psr\Http\Message\ResponseInterface */ public function submitAuthAPIRequest($apiMethod, $requestURI, $jsonBody, $headers) @@ -93,7 +93,7 @@ private function getAuthToken() * @param string $apiMethod * @param string $requestURI * @param string $jsonBody - * @param array $headers + * @param array $headers * @return \Psr\Http\Message\ResponseInterface */ private function submitAPIRequest($apiMethod, $requestURI, $jsonBody, $headers) diff --git a/src/Magento/FunctionalTestingFramework/Module/MagentoAssert.php b/src/Magento/FunctionalTestingFramework/Module/MagentoAssert.php index 494b4a6ed..3c6c08173 100644 --- a/src/Magento/FunctionalTestingFramework/Module/MagentoAssert.php +++ b/src/Magento/FunctionalTestingFramework/Module/MagentoAssert.php @@ -19,7 +19,7 @@ class MagentoAssert extends \Codeception\Module * Asserts that all items in the array are sorted by given direction. Can be given int, string, double, dates. * Converts given date strings to epoch for comparison. * - * @param array $data + * @param array $data * @param string $sortOrder * @return void */ diff --git a/src/Magento/FunctionalTestingFramework/Module/MagentoRestDriver.php b/src/Magento/FunctionalTestingFramework/Module/MagentoRestDriver.php index 419e16aa9..513d92dfb 100644 --- a/src/Magento/FunctionalTestingFramework/Module/MagentoRestDriver.php +++ b/src/Magento/FunctionalTestingFramework/Module/MagentoRestDriver.php @@ -49,7 +49,6 @@ * Conflicts with SOAP module * */ -// @codingStandardsIgnoreFile class MagentoRestDriver extends REST { /** @@ -60,14 +59,6 @@ class MagentoRestDriver extends REST const HTTP_METHOD_PUT = 'PUT'; const HTTP_METHOD_POST = 'POST'; - protected static $categoryEndpoint = 'categories'; - protected static $productEndpoint = 'products'; - protected static $productAttributesEndpoint = 'products/attributes'; - protected static $productAttributesOptionsEndpoint = 'products/attributes/%s/options'; - protected static $productAttributeSetEndpoint = 'products/attribute-sets/attributes'; - protected static $configurableProductEndpoint = 'configurable-products/%s/options'; - protected static $customersEndpoint = 'customers'; - /** * Module required fields. * @@ -101,6 +92,7 @@ class MagentoRestDriver extends REST * Before suite. * * @param array $settings + * @return void */ public function _beforeSuite($settings = []) { @@ -120,11 +112,14 @@ public function _beforeSuite($settings = []) $this->seeResponseCodeIs(\Codeception\Util\HttpCode::OK); $this->haveHttpHeader('Authorization', 'Bearer ' . $token); self::$adminTokens[$this->config['username']] = $token; + // @codingStandardsIgnoreStart $this->getModule('\Magento\FunctionalTestingFramework\Module\MagentoSequence')->_initialize(); + // @codingStandardsIgnoreEnd } /** * After suite. + * @return void */ public function _afterSuite() { @@ -135,17 +130,17 @@ public function _afterSuite() /** * Get admin auth token by username and password. * - * @param string $username - * @param string $password - * @param bool $newToken + * @param string $username + * @param string $password + * @param boolean $newToken * @return string * @part json * @part xml */ public function getAdminAuthToken($username = null, $password = null, $newToken = false) { - $username = !is_null($username) ? $username : $this->config['username']; - $password = !is_null($password) ? $password : $this->config['password']; + $username = $username !== null ? $username : $this->config['username']; + $password = $password !== null ? $password : $this->config['password']; // Use existing token if it exists if (!$newToken @@ -163,16 +158,17 @@ public function getAdminAuthToken($username = null, $password = null, $newToken /** * Admin token authentication for a given user. * - * @param string $username - * @param string $password - * @param bool $newToken + * @param string $username + * @param string $password + * @param boolean $newToken * @part json * @part xml + * @return void */ public function amAdminTokenAuthenticated($username = null, $password = null, $newToken = false) { - $username = !is_null($username) ? $username : $this->config['username']; - $password = !is_null($password) ? $password : $this->config['password']; + $username = $username !== null ? $username : $this->config['username']; + $password = $password !== null ? $password : $this->config['password']; $this->haveHttpHeader('Content-Type', 'application/json'); if ($newToken || !isset(self::$adminTokens[$username])) { @@ -187,11 +183,11 @@ public function amAdminTokenAuthenticated($username = null, $password = null, $n /** * Send REST API request. * - * @param string $endpoint - * @param string $httpMethod - * @param array $params - * @param string $grabByJsonPath - * @param bool $decode + * @param string $endpoint + * @param string $httpMethod + * @param array $params + * @param string $grabByJsonPath + * @param boolean $decode * @return mixed * @throws \LogicException * @part json @@ -218,7 +214,7 @@ public function sendRestRequest($endpoint, $httpMethod, $params = [], $grabByJso } $this->seeResponseCodeIs(\Codeception\Util\HttpCode::OK); - if (!$decode && is_null($grabByJsonPath)) { + if (!$decode && $grabByJsonPath === null) { return $this->grabResponse(); } elseif (!$decode) { return $this->grabDataFromResponseByJsonPath($grabByJsonPath); @@ -251,8 +247,8 @@ public function requireCategory($categoryData = []) /** * Create a simple product in Magento. * - * @param int $categoryId - * @param array $simpleProductData + * @param integer $categoryId + * @param array $simpleProductData * @return array|mixed * @part json * @part xml @@ -273,8 +269,8 @@ public function requireSimpleProduct($categoryId = 0, $simpleProductData = []) /** * Create a configurable product in Magento. * - * @param int $categoryId - * @param array $configurableProductData + * @param integer $categoryId + * @param array $configurableProductData * @return array|mixed * @part json * @part xml @@ -382,7 +378,7 @@ public function requireProductAttribute($code = 'attribute') /** * Create a customer in Magento. * - * @param array $customerData + * @param array $customerData * @param string $password * @return array|mixed * @part json @@ -445,9 +441,9 @@ public function getCategoryApiData($categoryData = []) /** * Get simple product api data. * - * @param string $type + * @param string $type * @param integer $categoryId - * @param array $productData + * @param array $productData * @return array * @part json * @part xml @@ -513,7 +509,7 @@ public function getCustomerApiData($customerData = []) /** * Get customer data including password. * - * @param array $customerData + * @param array $customerData * @param string $password * @return array * @part json @@ -526,7 +522,7 @@ public function getCustomerApiDataWithPassword($customerData = [], $password = ' /** * @param string $code - * @param array $attributeData + * @param array $attributeData * @return array * @part json * @part xml @@ -625,10 +621,10 @@ public function getConfigurableProductOptionsApiData($attributes, $optionIds) } /** - * @param array $configurableProductOptions - * @param array $childProductIds - * @param array $configurableProduct - * @param int $categoryId + * @param array $configurableProductOptions + * @param array $childProductIds + * @param array $configurableProduct + * @param integer $categoryId * @return array * @part json * @part xml @@ -655,9 +651,9 @@ public function getConfigurableProductApiData( } /** - * @param $attributeCode - * @param int $attributeSetId - * @param int $attributeGroupId + * @param string $attributeCode + * @param integer $attributeSetId + * @param integer $attributeGroupId * @return array * @part json * @part xml diff --git a/src/Magento/FunctionalTestingFramework/Module/MagentoSequence.php b/src/Magento/FunctionalTestingFramework/Module/MagentoSequence.php index df59a62ec..6273965dd 100644 --- a/src/Magento/FunctionalTestingFramework/Module/MagentoSequence.php +++ b/src/Magento/FunctionalTestingFramework/Module/MagentoSequence.php @@ -3,7 +3,8 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ - + +// @codingStandardsIgnoreFile namespace Magento\FunctionalTestingFramework\Module; use Codeception\Module\Sequence; @@ -12,13 +13,11 @@ /** * MagentoSequence module. * - * @codingStandardsIgnoreFile */ class MagentoSequence extends Sequence { protected $config = ['prefix' => '']; } - if (!function_exists('msq') && !function_exists('msqs')) { require_once __DIR__ . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . 'Util' . DIRECTORY_SEPARATOR . 'msq.php'; } else { diff --git a/src/Magento/FunctionalTestingFramework/Module/MagentoWebDriver.php b/src/Magento/FunctionalTestingFramework/Module/MagentoWebDriver.php index a1815aa70..3067ac7bc 100644 --- a/src/Magento/FunctionalTestingFramework/Module/MagentoWebDriver.php +++ b/src/Magento/FunctionalTestingFramework/Module/MagentoWebDriver.php @@ -45,10 +45,14 @@ * browser: chrome * ``` */ -// @codingStandardsIgnoreFile class MagentoWebDriver extends WebDriver { use AttachmentSupport; + + /** + * List of known magento loading masks by selector + * @var array + */ public static $loadingMasksLocators = [ '//div[contains(@class, "loading-mask")]', '//div[contains(@class, "admin_data-grid-loading-mask")]', @@ -105,12 +109,21 @@ class MagentoWebDriver extends WebDriver */ private $htmlReport; + /** + * Sanitizes config, then initializes using parent. + * @return void + */ public function _initialize() { $this->config = ConfigSanitizerUtil::sanitizeWebDriverConfig($this->config); parent::_initialize(); } + /** + * Calls parent reset, then re-sanitizes config + * + * @return void + */ public function _resetConfig() { parent::_resetConfig(); @@ -250,18 +263,19 @@ public function closeAdminNotification() // Cheating here for the minute. Still working on the best method to deal with this issue. try { $this->executeJS("jQuery('.modal-popup').remove(); jQuery('.modals-overlay').remove();"); - } catch (\Exception $e) {} + } catch (\Exception $e) { + } } - /** * Search for and Select multiple options from a Magento Multi-Select drop down menu. * e.g. The drop down menu you use to assign Products to Categories. * - * @param $select - * @param array $options - * @param bool $requireAction + * @param string $select + * @param array $options + * @param boolean $requireAction * @throws \Exception + * @return void */ public function searchAndMultiSelectOption($select, array $options, $requireAction = false) { @@ -286,8 +300,8 @@ public function searchAndMultiSelectOption($select, array $options, $requireActi /** * Select multiple options from a drop down using a filter and text field to narrow results. * - * @param string $selectSearchTextField - * @param string $selectSearchResult + * @param string $selectSearchTextField + * @param string $selectSearchResult * @param string[] $options * @return void */ @@ -306,7 +320,8 @@ public function selectMultipleOptions($selectSearchTextField, $selectSearchResul /** * Wait for all Ajax calls to finish. * - * @param int $timeout + * @param integer $timeout + * @return void */ public function waitForAjaxLoad($timeout = null) { @@ -324,8 +339,9 @@ public function waitForAjaxLoad($timeout = null) /** * Wait for all JavaScript to finish executing. * - * @param int $timeout + * @param integer $timeout * @throws \Exception + * @return void */ public function waitForPageLoad($timeout = null) { @@ -340,10 +356,11 @@ public function waitForPageLoad($timeout = null) * Wait for all visible loading masks to disappear. Gets all elements by mask selector, then loops over them. * * @throws \Exception + * @return void */ public function waitForLoadingMaskToDisappear() { - foreach( self::$loadingMasksLocators as $maskLocator) { + foreach (self::$loadingMasksLocators as $maskLocator) { // Get count of elements found for looping. // Elements are NOT useful for interaction, as they cannot be fed to codeception actions. $loadingMaskElements = $this->_findElements($maskLocator); @@ -359,6 +376,7 @@ public function waitForLoadingMaskToDisappear() * Verify that there are no JavaScript errors in the console. * * @throws ModuleException + * @return void */ public function dontSeeJsError() { @@ -371,7 +389,7 @@ public function dontSeeJsError() } /** - * @param float $money + * @param float $money * @param string $locale * @return array */ @@ -391,14 +409,16 @@ public function formatMoney(float $money, $locale = 'en_US.UTF-8') * @param string $floatString * @return float */ - public function parseFloat($floatString){ + public function parseFloat($floatString) + { $floatString = str_replace(',', '', $floatString); return floatval($floatString); } /** - * @param int $category - * @param string $locale + * @param integer $category + * @param string $locale + * @return void */ public function mSetLocale(int $category, $locale) { @@ -413,11 +433,12 @@ public function mSetLocale(int $category, $locale) /** * Reset Locale setting. + * @return void */ public function mResetLocale() { foreach (self::$localeAll as $c => $l) { - if (!is_null($l)) { + if ($l !== null) { setlocale($c, $l); self::$localeAll[$c] = null; } @@ -426,6 +447,7 @@ public function mResetLocale() /** * Scroll to the Top of the Page. + * @return void */ public function scrollToTopOfPage() { @@ -435,7 +457,7 @@ public function scrollToTopOfPage() /** * Takes given $command and executes it against exposed MTF CLI entry point. Returns response from server. * @param string $command - * @returns string + * @return string */ public function magentoCLI($command) { @@ -464,10 +486,11 @@ public function deleteEntityByUrl($url) /** * Conditional click for an area that should be visible * - * @param string $selector - * @param string $dependentSelector - * @param bool $visible + * @param string $selector + * @param string $dependentSelector + * @param boolean $visible * @throws \Exception + * @return void */ public function conditionalClick($selector, $dependentSelector, $visible) { @@ -492,6 +515,7 @@ public function conditionalClick($selector, $dependentSelector, $visible) * Clear the given Text Field or Textarea * * @param string $selector + * @return void */ public function clearField($selector) { @@ -503,7 +527,8 @@ public function clearField($selector) * * @param string $selector * @param string $attribute - * @param $value + * @param string $value + * @return void */ public function assertElementContainsAttribute($selector, $attribute, $value) { @@ -518,6 +543,11 @@ public function assertElementContainsAttribute($selector, $attribute, $value) } } + /** + * Sets current test to the given test, and resets test failure artifacts to null + * @param TestInterface $test + * @return void + */ public function _before(TestInterface $test) { $this->current_test = $test; @@ -529,10 +559,10 @@ public function _before(TestInterface $test) /** * Override for codeception's default dragAndDrop to include offset options. - * @param string $source - * @param string $target - * @param int $xOffset - * @param int $yOffset + * @param string $source + * @param string $target + * @param integer $xOffset + * @param integer $yOffset * @return void */ public function dragAndDrop($source, $target, $xOffset = null, $yOffset = null) @@ -562,7 +592,8 @@ public function dragAndDrop($source, $target, $xOffset = null, $yOffset = null) * following parent execution of test failure processing. * * @param TestInterface $test - * @param \Exception $fail + * @param \Exception $fail + * @return void */ public function _failed(TestInterface $test, $fail) { @@ -586,6 +617,7 @@ public function _failed(TestInterface $test, $fail) /** * Function which saves a screenshot of the current stat of the browser + * @return void */ public function saveScreenshot() { diff --git a/src/Magento/FunctionalTestingFramework/ObjectManager.php b/src/Magento/FunctionalTestingFramework/ObjectManager.php index 51b64dd29..8b0f34537 100644 --- a/src/Magento/FunctionalTestingFramework/ObjectManager.php +++ b/src/Magento/FunctionalTestingFramework/ObjectManager.php @@ -34,9 +34,9 @@ class ObjectManager extends \Magento\FunctionalTestingFramework\ObjectManager\Ob /** * ObjectManager constructor. - * @param ObjectManager\Factory|null $factory + * @param ObjectManager\Factory|null $factory * @param ObjectManager\ConfigInterface|null $config - * @param array $sharedInstances + * @param array $sharedInstances */ public function __construct( \Magento\FunctionalTestingFramework\ObjectManager\Factory $factory = null, @@ -64,7 +64,7 @@ public function getParameters($type, $method) * * @param object $object * @param string $method - * @param array $arguments + * @param array $arguments * @return array */ public function prepareArguments($object, $method, array $arguments = []) @@ -101,7 +101,7 @@ public static function setInstance(ObjectManager $objectManager) /** * Retrieve object manager * - * @return ObjectManager|bool + * @return ObjectManager|boolean * @throws \RuntimeException */ public static function getInstance() diff --git a/src/Magento/FunctionalTestingFramework/ObjectManager/Config.php b/src/Magento/FunctionalTestingFramework/ObjectManager/Config.php index fdc8ca373..3133de1ef 100644 --- a/src/Magento/FunctionalTestingFramework/ObjectManager/Config.php +++ b/src/Magento/FunctionalTestingFramework/ObjectManager/Config.php @@ -27,7 +27,7 @@ class Config extends ObjectManagerConfig * Check whether type is shared * * @param string $type - * @return bool + * @return boolean * @SuppressWarnings(PHPMD.UnusedLocalVariable) */ public function isShared($type) diff --git a/src/Magento/FunctionalTestingFramework/ObjectManager/Config/Config.php b/src/Magento/FunctionalTestingFramework/ObjectManager/Config/Config.php index 502dba0c0..c8e6c8292 100644 --- a/src/Magento/FunctionalTestingFramework/ObjectManager/Config/Config.php +++ b/src/Magento/FunctionalTestingFramework/ObjectManager/Config/Config.php @@ -73,7 +73,7 @@ class Config implements \Magento\FunctionalTestingFramework\ObjectManager\Config /** * Config constructor. - * @param RelationsInterface|null $relations + * @param RelationsInterface|null $relations * @param DefinitionInterface|null $definitions */ public function __construct(RelationsInterface $relations = null, DefinitionInterface $definitions = null) @@ -99,7 +99,7 @@ public function getArguments($type) * Check whether type is shared * * @param string $type - * @return bool + * @return boolean */ public function isShared($type) { diff --git a/src/Magento/FunctionalTestingFramework/ObjectManager/Config/Reader/DomFactory.php b/src/Magento/FunctionalTestingFramework/ObjectManager/Config/Reader/DomFactory.php index 78e977625..27d5518ad 100644 --- a/src/Magento/FunctionalTestingFramework/ObjectManager/Config/Reader/DomFactory.php +++ b/src/Magento/FunctionalTestingFramework/ObjectManager/Config/Reader/DomFactory.php @@ -30,7 +30,7 @@ class DomFactory * Factory constructor * * @param \Magento\FunctionalTestingFramework\ObjectManagerInterface $objectManager - * @param string $instanceName + * @param string $instanceName */ public function __construct( \Magento\FunctionalTestingFramework\ObjectManagerInterface $objectManager, diff --git a/src/Magento/FunctionalTestingFramework/ObjectManager/ConfigInterface.php b/src/Magento/FunctionalTestingFramework/ObjectManager/ConfigInterface.php index bec418fc0..53f3ba95a 100644 --- a/src/Magento/FunctionalTestingFramework/ObjectManager/ConfigInterface.php +++ b/src/Magento/FunctionalTestingFramework/ObjectManager/ConfigInterface.php @@ -22,7 +22,7 @@ public function getArguments($type); * Check whether type is shared * * @param string $type - * @return bool + * @return boolean */ public function isShared($type); diff --git a/src/Magento/FunctionalTestingFramework/ObjectManager/Factory.php b/src/Magento/FunctionalTestingFramework/ObjectManager/Factory.php index b377139e2..9b795d58f 100644 --- a/src/Magento/FunctionalTestingFramework/ObjectManager/Factory.php +++ b/src/Magento/FunctionalTestingFramework/ObjectManager/Factory.php @@ -24,10 +24,10 @@ class Factory extends \Magento\FunctionalTestingFramework\ObjectManager\Factory\ /** * Factory constructor. - * @param ConfigInterface $config + * @param ConfigInterface $config * @param \Magento\FunctionalTestingFramework\ObjectManagerInterface|null $objectManager - * @param DefinitionInterface|null $definitions - * @param array $globalArguments + * @param DefinitionInterface|null $definitions + * @param array $globalArguments */ public function __construct( ConfigInterface $config, @@ -77,7 +77,7 @@ public function getParameters($type, $method) * * @param object $object * @param string $method - * @param array $arguments + * @param array $arguments * @return array */ public function prepareArguments($object, $method, array $arguments = []) @@ -95,8 +95,8 @@ public function prepareArguments($object, $method, array $arguments = []) * Resolve constructor arguments * * @param string $requestedType - * @param array $parameters - * @param array $arguments + * @param array $parameters + * @param array $arguments * @return array * @throws \UnexpectedValueException * @throws \BadMethodCallException @@ -177,7 +177,7 @@ protected function resolveArguments($requestedType, array $parameters, array $ar /** * Parse array argument * - * @param array &$array + * @param array $array * @return void * @SuppressWarnings(PHPMD.CyclomaticComplexity) * @SuppressWarnings(PHPMD.NPathComplexity) @@ -216,7 +216,7 @@ protected function parseArray(&$array) * Create instance with call time arguments * * @param string $requestedType - * @param array $arguments + * @param array $arguments * @return object * @throws \Exception * diff --git a/src/Magento/FunctionalTestingFramework/ObjectManager/Factory/Dynamic/Developer.php b/src/Magento/FunctionalTestingFramework/ObjectManager/Factory/Dynamic/Developer.php index 5e452e55f..dbe6aa497 100644 --- a/src/Magento/FunctionalTestingFramework/ObjectManager/Factory/Dynamic/Developer.php +++ b/src/Magento/FunctionalTestingFramework/ObjectManager/Factory/Dynamic/Developer.php @@ -47,10 +47,10 @@ class Developer implements \Magento\FunctionalTestingFramework\ObjectManager\Fac /** * Developer constructor. - * @param \Magento\FunctionalTestingFramework\ObjectManager\ConfigInterface $config - * @param \Magento\FunctionalTestingFramework\ObjectManagerInterface|null $objectManager + * @param \Magento\FunctionalTestingFramework\ObjectManager\ConfigInterface $config + * @param \Magento\FunctionalTestingFramework\ObjectManagerInterface|null $objectManager * @param \Magento\FunctionalTestingFramework\ObjectManager\DefinitionInterface|null $definitions - * @param array $globalArguments + * @param array $globalArguments */ public function __construct( \Magento\FunctionalTestingFramework\ObjectManager\ConfigInterface $config, @@ -79,8 +79,8 @@ public function setObjectManager(\Magento\FunctionalTestingFramework\ObjectManag * Resolve constructor arguments * * @param string $requestedType - * @param array $parameters - * @param array $arguments + * @param array $parameters + * @param array $arguments * @return array * @throws \UnexpectedValueException * @throws \BadMethodCallException @@ -139,7 +139,7 @@ protected function resolveArguments($requestedType, array $parameters, array $ar /** * Parse array argument * - * @param array &$array + * @param array $array * @return void */ protected function parseArray(&$array) @@ -167,7 +167,7 @@ protected function parseArray(&$array) * Create instance with call time arguments * * @param string $requestedType - * @param array $arguments + * @param array $arguments * @return object * @throws \Exception * diff --git a/src/Magento/FunctionalTestingFramework/ObjectManager/FactoryInterface.php b/src/Magento/FunctionalTestingFramework/ObjectManager/FactoryInterface.php index eca6f5bb1..b1ad87597 100644 --- a/src/Magento/FunctionalTestingFramework/ObjectManager/FactoryInterface.php +++ b/src/Magento/FunctionalTestingFramework/ObjectManager/FactoryInterface.php @@ -14,7 +14,7 @@ interface FactoryInterface * Create instance with call time arguments * * @param string $requestedType - * @param array $arguments + * @param array $arguments * @return object * @throws \LogicException * @throws \BadMethodCallException diff --git a/src/Magento/FunctionalTestingFramework/ObjectManager/ObjectManager.php b/src/Magento/FunctionalTestingFramework/ObjectManager/ObjectManager.php index ff177e87a..57c5cc39c 100644 --- a/src/Magento/FunctionalTestingFramework/ObjectManager/ObjectManager.php +++ b/src/Magento/FunctionalTestingFramework/ObjectManager/ObjectManager.php @@ -13,7 +13,7 @@ class ObjectManager implements \Magento\FunctionalTestingFramework\ObjectManager { /** * Create instance with call time arguments. - * + * * @var \Magento\FunctionalTestingFramework\ObjectManager\FactoryInterface */ protected $factory; @@ -35,8 +35,8 @@ class ObjectManager implements \Magento\FunctionalTestingFramework\ObjectManager /** * ObjectManager constructor. * @param FactoryInterface $factory - * @param ConfigInterface $config - * @param array $sharedInstances + * @param ConfigInterface $config + * @param array $sharedInstances */ public function __construct(FactoryInterface $factory, ConfigInterface $config, array $sharedInstances = []) { @@ -50,7 +50,7 @@ public function __construct(FactoryInterface $factory, ConfigInterface $config, * Create new object instance * * @param string $type - * @param array $arguments + * @param array $arguments * @return object */ public function create($type, array $arguments = []) diff --git a/src/Magento/FunctionalTestingFramework/ObjectManager/Relations/Runtime.php b/src/Magento/FunctionalTestingFramework/ObjectManager/Relations/Runtime.php index 4da8b6de0..192758220 100644 --- a/src/Magento/FunctionalTestingFramework/ObjectManager/Relations/Runtime.php +++ b/src/Magento/FunctionalTestingFramework/ObjectManager/Relations/Runtime.php @@ -37,7 +37,7 @@ public function __construct(\Magento\FunctionalTestingFramework\Code\Reader\Clas * Check whether requested type is available for read * * @param string $type - * @return bool + * @return boolean */ public function has($type) { diff --git a/src/Magento/FunctionalTestingFramework/ObjectManager/RelationsInterface.php b/src/Magento/FunctionalTestingFramework/ObjectManager/RelationsInterface.php index 51245710f..b1ff6b72b 100644 --- a/src/Magento/FunctionalTestingFramework/ObjectManager/RelationsInterface.php +++ b/src/Magento/FunctionalTestingFramework/ObjectManager/RelationsInterface.php @@ -14,7 +14,7 @@ interface RelationsInterface * Check whether requested type is available for read * * @param string $type - * @return bool + * @return boolean */ public function has($type); diff --git a/src/Magento/FunctionalTestingFramework/ObjectManagerInterface.php b/src/Magento/FunctionalTestingFramework/ObjectManagerInterface.php index 4e7c9da1d..53cfc8d8e 100644 --- a/src/Magento/FunctionalTestingFramework/ObjectManagerInterface.php +++ b/src/Magento/FunctionalTestingFramework/ObjectManagerInterface.php @@ -15,7 +15,7 @@ interface ObjectManagerInterface * Create new object instance * * @param string $type - * @param array $arguments + * @param array $arguments * @return object */ public function create($type, array $arguments = []); diff --git a/src/Magento/FunctionalTestingFramework/Page/Config/Dom.php b/src/Magento/FunctionalTestingFramework/Page/Config/Dom.php index 1a4f37d4c..0226a286a 100644 --- a/src/Magento/FunctionalTestingFramework/Page/Config/Dom.php +++ b/src/Magento/FunctionalTestingFramework/Page/Config/Dom.php @@ -37,13 +37,13 @@ class Dom extends \Magento\FunctionalTestingFramework\Config\MftfDom /** * Page Dom constructor. - * @param string $xml - * @param string $filename + * @param string $xml + * @param string $filename * @param ExceptionCollector $exceptionCollector - * @param array $idAttributes - * @param string $typeAttributeName - * @param string $schemaFile - * @param string $errorFormat + * @param array $idAttributes + * @param string $typeAttributeName + * @param string $schemaFile + * @param string $errorFormat */ public function __construct( $xml, @@ -70,7 +70,7 @@ public function __construct( /** * Takes a dom element from xml and appends the filename based on location * - * @param string $xml + * @param string $xml * @param string|null $filename * @return \DOMDocument */ diff --git a/src/Magento/FunctionalTestingFramework/Page/Config/SectionDom.php b/src/Magento/FunctionalTestingFramework/Page/Config/SectionDom.php index d0144a9d4..7115adb34 100644 --- a/src/Magento/FunctionalTestingFramework/Page/Config/SectionDom.php +++ b/src/Magento/FunctionalTestingFramework/Page/Config/SectionDom.php @@ -29,13 +29,13 @@ class SectionDom extends \Magento\FunctionalTestingFramework\Config\MftfDom /** * Entity Dom constructor. - * @param string $xml - * @param string $filename + * @param string $xml + * @param string $filename * @param ExceptionCollector $exceptionCollector - * @param array $idAttributes - * @param string $typeAttributeName - * @param string $schemaFile - * @param string $errorFormat + * @param array $idAttributes + * @param string $typeAttributeName + * @param string $schemaFile + * @param string $errorFormat */ public function __construct( $xml, @@ -61,7 +61,7 @@ public function __construct( /** * Takes a dom element from xml and appends the filename based on location * - * @param string $xml + * @param string $xml * @param string|null $filename * @return \DOMDocument */ diff --git a/src/Magento/FunctionalTestingFramework/Page/Handlers/SectionObjectHandler.php b/src/Magento/FunctionalTestingFramework/Page/Handlers/SectionObjectHandler.php index ec52e6eb5..459f4a9ef 100644 --- a/src/Magento/FunctionalTestingFramework/Page/Handlers/SectionObjectHandler.php +++ b/src/Magento/FunctionalTestingFramework/Page/Handlers/SectionObjectHandler.php @@ -108,7 +108,7 @@ public static function getInstance() /** * Get a SectionObject by name * - * @param string $name The section name + * @param string $name The section name. * @return SectionObject | null */ public function getObject($name) diff --git a/src/Magento/FunctionalTestingFramework/Page/Objects/ElementObject.php b/src/Magento/FunctionalTestingFramework/Page/Objects/ElementObject.php index 7a18c76dd..6b0626f10 100644 --- a/src/Magento/FunctionalTestingFramework/Page/Objects/ElementObject.php +++ b/src/Magento/FunctionalTestingFramework/Page/Objects/ElementObject.php @@ -58,12 +58,12 @@ class ElementObject /** * ElementObject constructor. - * @param string $name - * @param string $type - * @param string $selector - * @param string $locatorFunction - * @param string $timeout - * @param bool $parameterized + * @param string $name + * @param string $type + * @param string $selector + * @param string $locatorFunction + * @param string $timeout + * @param boolean $parameterized * @throws XmlException */ public function __construct($name, $type, $selector, $locatorFunction, $timeout, $parameterized) @@ -138,7 +138,7 @@ public function getPrioritizedSelector() /** * Returns an integer representing an element's timeout * - * @return int|null + * @return integer|null */ public function getTimeout() { @@ -152,9 +152,8 @@ public function getTimeout() /** * Determines if the element's selector is parameterized. Based on $parameterized property. * - * @return bool + * @return boolean */ - public function isParameterized() { return $this->parameterized; diff --git a/src/Magento/FunctionalTestingFramework/Page/Objects/PageObject.php b/src/Magento/FunctionalTestingFramework/Page/Objects/PageObject.php index 80e29496d..3533a13f3 100644 --- a/src/Magento/FunctionalTestingFramework/Page/Objects/PageObject.php +++ b/src/Magento/FunctionalTestingFramework/Page/Objects/PageObject.php @@ -6,7 +6,6 @@ namespace Magento\FunctionalTestingFramework\Page\Objects; - use Magento\FunctionalTestingFramework\Exceptions\XmlException; use Magento\FunctionalTestingFramework\Page\Handlers\SectionObjectHandler; @@ -61,12 +60,12 @@ class PageObject /** * PageObject constructor. - * @param string $name - * @param string $url - * @param string $module - * @param array $sections - * @param bool $parameterized - * @param string $area + * @param string $name + * @param string $url + * @param string $module + * @param array $sections + * @param boolean $parameterized + * @param string $area */ public function __construct($name, $url, $module, $sections, $parameterized, $area) { @@ -158,9 +157,8 @@ public function getSection($sectionName) /** * Determines if the page's url is parameterized. Based on $parameterized property. * - * @return bool + * @return boolean */ - public function isParameterized() { return $this->parameterized; diff --git a/src/Magento/FunctionalTestingFramework/Page/Objects/SectionObject.php b/src/Magento/FunctionalTestingFramework/Page/Objects/SectionObject.php index 121d3e6ca..9ac641bca 100644 --- a/src/Magento/FunctionalTestingFramework/Page/Objects/SectionObject.php +++ b/src/Magento/FunctionalTestingFramework/Page/Objects/SectionObject.php @@ -28,7 +28,7 @@ class SectionObject /** * SectionObject constructor. * @param string $name - * @param array $elements + * @param array $elements */ public function __construct($name, $elements) { @@ -59,7 +59,7 @@ public function getElements() /** * Checks to see if this section contains any element by the name of elementName * @param string $elementName - * @return bool + * @return boolean */ public function hasElement($elementName) { diff --git a/src/Magento/FunctionalTestingFramework/Suite/Generators/GroupClassGenerator.php b/src/Magento/FunctionalTestingFramework/Suite/Generators/GroupClassGenerator.php index d07f1e987..2b9662a1a 100644 --- a/src/Magento/FunctionalTestingFramework/Suite/Generators/GroupClassGenerator.php +++ b/src/Magento/FunctionalTestingFramework/Suite/Generators/GroupClassGenerator.php @@ -173,7 +173,7 @@ private function buildHookMustacheArray($hookObj) * appends the entry to the given array. The result is returned by the function. * * @param ActionObject $action - * @param array $actionEntries + * @param array $actionEntries * @return array * @throws TestReferenceException */ @@ -194,7 +194,7 @@ private function buildWebDriverActionsMustacheArray($action, $actionEntries) * for the generated step. * * @param string $formattedStep - * @param array $actionEntries + * @param array $actionEntries * @param string $actor * @return array */ @@ -217,7 +217,7 @@ private function replaceReservedTesterFunctions($formattedStep, $actionEntries, * Takes an action object of persistence type and formats an array entiry for mustache template interpretation. * * @param ActionObject $action - * @param array $entityArray + * @param array $entityArray * @return array */ private function buildPersistenceMustacheArray($action, $entityArray) diff --git a/src/Magento/FunctionalTestingFramework/Suite/Objects/SuiteObject.php b/src/Magento/FunctionalTestingFramework/Suite/Objects/SuiteObject.php index bf560eac3..d7f6bd18a 100644 --- a/src/Magento/FunctionalTestingFramework/Suite/Objects/SuiteObject.php +++ b/src/Magento/FunctionalTestingFramework/Suite/Objects/SuiteObject.php @@ -43,9 +43,9 @@ class SuiteObject /** * SuiteObject constructor. - * @param string $name - * @param TestObject[] $includeTests - * @param TestObject[] $excludeTests + * @param string $name + * @param TestObject[] $includeTests + * @param TestObject[] $excludeTests * @param TestHookObject[] $hooks */ public function __construct($name, $includeTests, $excludeTests, $hooks) @@ -110,7 +110,7 @@ private function resolveTests($includeTests, $excludeTests) * Convenience method for determining if a Suite will require group file generation. * A group file will only be generated when the user specifies a before/after statement. * - * @return bool + * @return boolean */ public function requiresGroupFile() { diff --git a/src/Magento/FunctionalTestingFramework/Suite/SuiteGenerator.php b/src/Magento/FunctionalTestingFramework/Suite/SuiteGenerator.php index 4e0ef32df..7f3efdbfc 100644 --- a/src/Magento/FunctionalTestingFramework/Suite/SuiteGenerator.php +++ b/src/Magento/FunctionalTestingFramework/Suite/SuiteGenerator.php @@ -114,7 +114,7 @@ public function generateSuite($suiteName) * run so that any pre/post conditions can be duplicated. * * @param string $suiteName - * @param array $tests + * @param array $tests * @param string $originalSuiteName * @return void * @throws TestReferenceException @@ -152,7 +152,7 @@ private function generateSuiteFromTest($suiteName, $tests = [], $originalSuiteNa * prevent possible invalid test configurations from executing. * * @param string $suiteName - * @param array $testsReferenced + * @param array $testsReferenced * @param string $originalSuiteName * @return void * @throws TestReferenceException @@ -176,7 +176,7 @@ private function validateTestsReferencedInSuite($suiteName, $testsReferenced, $o * and generates applicable suites. * * @param string $suiteName - * @param array $suiteContent + * @param array $suiteContent * @return void * @throws \Exception */ @@ -192,7 +192,7 @@ private function generateSplitSuiteFromTest($suiteName, $suiteContent) * and generates a group file which captures suite level preconditions. * * @param string $suiteName - * @param array $tests + * @param array $tests * @param string $originalSuiteName * @return null|string * @throws XmlException @@ -273,13 +273,11 @@ private static function clearPreviousSessionConfigEntries() if (preg_match('/(Group\\\\.*)/', $entry)) { unset($newYmlArray[self::YAML_EXTENSIONS_TAG][self::YAML_ENABLED_TAG][$key]); } - } // needed for proper yml file generation based on indices $newYmlArray[self::YAML_EXTENSIONS_TAG][self::YAML_ENABLED_TAG] = array_values($newYmlArray[self::YAML_EXTENSIONS_TAG][self::YAML_ENABLED_TAG]); - } if (array_key_exists(self::YAML_GROUPS_TAG, $newYmlArray)) { @@ -296,7 +294,7 @@ private static function clearPreviousSessionConfigEntries() * generator which is then called to create all the test files for the suite. * * @param string $path - * @param array $tests + * @param array $tests * @return void * @throws TestReferenceException */ diff --git a/src/Magento/FunctionalTestingFramework/Suite/Util/SuiteObjectExtractor.php b/src/Magento/FunctionalTestingFramework/Suite/Util/SuiteObjectExtractor.php index 4f72042bf..642671246 100644 --- a/src/Magento/FunctionalTestingFramework/Suite/Util/SuiteObjectExtractor.php +++ b/src/Magento/FunctionalTestingFramework/Suite/Util/SuiteObjectExtractor.php @@ -200,7 +200,7 @@ private function extractModuleAndFiles($moduleName, $moduleFilePath) * Takes a filepath (and optionally a module name) and resolves to a test object. * * @param string $filename - * @param null $moduleName + * @param null $moduleName * @return TestObject[] * @throws Exception */ diff --git a/src/Magento/FunctionalTestingFramework/Test/Config/ActionGroupDom.php b/src/Magento/FunctionalTestingFramework/Test/Config/ActionGroupDom.php index ec9b3e1a0..ab8bc66f5 100644 --- a/src/Magento/FunctionalTestingFramework/Test/Config/ActionGroupDom.php +++ b/src/Magento/FunctionalTestingFramework/Test/Config/ActionGroupDom.php @@ -20,7 +20,7 @@ class ActionGroupDom extends Dom * Takes a dom element from xml and appends the filename based on location while also validating the action group * step key. * - * @param string $xml + * @param string $xml * @param string|null $filename * @return \DOMDocument */ diff --git a/src/Magento/FunctionalTestingFramework/Test/Config/Converter/Dom/Flat.php b/src/Magento/FunctionalTestingFramework/Test/Config/Converter/Dom/Flat.php index db4fec886..dd72264b9 100644 --- a/src/Magento/FunctionalTestingFramework/Test/Config/Converter/Dom/Flat.php +++ b/src/Magento/FunctionalTestingFramework/Test/Config/Converter/Dom/Flat.php @@ -65,7 +65,7 @@ public function convert($source) * ) * * @param \DOMNode $source - * @param string $basePath + * @param string $basePath * @return string|array * @throws \UnexpectedValueException * @SuppressWarnings(PHPMD.CyclomaticComplexity) diff --git a/src/Magento/FunctionalTestingFramework/Test/Config/Dom.php b/src/Magento/FunctionalTestingFramework/Test/Config/Dom.php index df0dbf36e..ae301b5b1 100644 --- a/src/Magento/FunctionalTestingFramework/Test/Config/Dom.php +++ b/src/Magento/FunctionalTestingFramework/Test/Config/Dom.php @@ -40,13 +40,13 @@ class Dom extends \Magento\FunctionalTestingFramework\Config\MftfDom /** * Metadata Dom constructor. - * @param string $xml - * @param string $filename + * @param string $xml + * @param string $filename * @param ExceptionCollector $exceptionCollector - * @param array $idAttributes - * @param string $typeAttributeName - * @param string $schemaFile - * @param string $errorFormat + * @param array $idAttributes + * @param string $typeAttributeName + * @param string $schemaFile + * @param string $errorFormat */ public function __construct( $xml, @@ -73,7 +73,7 @@ public function __construct( /** * Takes a dom element from xml and appends the filename based on location * - * @param string $xml + * @param string $xml * @param string|null $filename * @return \DOMDocument */ @@ -131,9 +131,9 @@ public function initDom($xml, $filename = null) * Parses DOM Structure's actions and appends a before/after attribute along with the parent's stepkey reference. * * @param \DOMElement $testNode - * @param string $insertType - * @param string $insertKey - * @param string $filename + * @param string $insertType + * @param string $insertKey + * @param string $filename * @return void */ protected function appendMergePointerToActions($testNode, $insertType, $insertKey, $filename) diff --git a/src/Magento/FunctionalTestingFramework/Test/Objects/ActionGroupObject.php b/src/Magento/FunctionalTestingFramework/Test/Objects/ActionGroupObject.php index a75fc3145..659cc0a74 100644 --- a/src/Magento/FunctionalTestingFramework/Test/Objects/ActionGroupObject.php +++ b/src/Magento/FunctionalTestingFramework/Test/Objects/ActionGroupObject.php @@ -50,17 +50,17 @@ class ActionGroupObject /** * String of parent Action Group * - * @var String + * @var string */ private $parentActionGroup; /** * ActionGroupObject constructor. * - * @param string $name + * @param string $name * @param ArgumentObject[] $arguments - * @param array $actions - * @param string $parentActionGroup + * @param array $actions + * @param string $parentActionGroup */ public function __construct($name, $arguments, $actions, $parentActionGroup) { @@ -78,7 +78,7 @@ public function __construct($name, $arguments, $actions, $parentActionGroup) /** * Gets the ordered steps including merged waits * - * @param array $arguments + * @param array $arguments * @param string $actionReferenceKey * @return array * @throws TestReferenceException @@ -129,7 +129,7 @@ private function resolveArguments($arguments) * Function which takes a set of arguments to be appended to an action objects fields returns resulting * action objects with proper argument.field references. * - * @param array $arguments + * @param array $arguments * @param string $actionReferenceKey * @return array */ @@ -191,7 +191,6 @@ private function resolveAttributesWithArguments($arguments, $attributes) $newActionAttributes = []; foreach ($attributes as $attributeKey => $attributeValue) { - if (is_array($attributeValue)) { // attributes with child elements are parsed as an array, need make recursive call to resolve children $newActionAttributes[$attributeKey] = $this->resolveAttributesWithArguments( @@ -217,16 +216,15 @@ private function resolveAttributesWithArguments($arguments, $attributes) ); } return $newActionAttributes; - } /** * Function that takes an array of replacement arguments, and matches them with args in an actionGroup's attribute. * Determines if the replacement arguments are persisted data, and replaces them accordingly. * - * @param array $arguments + * @param array $arguments * @param string $attributeValue - * @param array $matches + * @param array $matches * @return string */ private function replaceAttributeArguments($arguments, $attributeValue, $matches) @@ -257,10 +255,10 @@ private function replaceAttributeArguments($arguments, $attributeValue, $matches /** * Replace attribute arguments in variable. * - * @param string $variable - * @param array $arguments - * @param string $attributeValue - * @param bool $isInnerArgument + * @param string $variable + * @param array $arguments + * @param string $attributeValue + * @param boolean $isInnerArgument * @return string */ private function replaceAttributeArgumentInVariable( @@ -313,9 +311,9 @@ private function replaceAttributeArgumentInVariable( /** * Replaces any arguments that were declared as simpleData="true". * Takes in isInnerArgument to determine what kind of replacement to expect: {{data}} vs section.element(data) - * @param string $argumentValue - * @param string $variableName - * @param string $attributeValue + * @param string $argumentValue + * @param string $variableName + * @param string $attributeValue * @param boolean $isInnerArgument * @return string */ @@ -330,10 +328,10 @@ private function replaceSimpleArgument($argumentValue, $variableName, $attribute /** * Replaces args with replacements given, behavior is specific to persisted arguments. - * @param string $replacement - * @param string $attributeValue - * @param string $fullVariable - * @param string $variable + * @param string $replacement + * @param string $attributeValue + * @param string $fullVariable + * @param string $variable * @param boolean $isParameter * @return string */ @@ -422,7 +420,7 @@ public function getArguments() /** * Searches through ActionGroupObject's arguments and returns first argument wi * @param string $name - * @param array $argumentList + * @param array $argumentList * @return ArgumentObject|null */ private function findArgumentByName($name, $argumentList) @@ -443,7 +441,7 @@ function ($e) use ($name) { * Replaces references to step keys used earlier in an action group * * @param ActionObject $action - * @param array $replacementStepKeys + * @param array $replacementStepKeys * @return ActionObject[] */ private function replaceCreateDataKeys($action, $replacementStepKeys) diff --git a/src/Magento/FunctionalTestingFramework/Test/Objects/ActionObject.php b/src/Magento/FunctionalTestingFramework/Test/Objects/ActionObject.php index 6b361daa0..75ebd7868 100644 --- a/src/Magento/FunctionalTestingFramework/Test/Objects/ActionObject.php +++ b/src/Magento/FunctionalTestingFramework/Test/Objects/ActionObject.php @@ -123,12 +123,12 @@ class ActionObject /** * ActionObject constructor. * - * @param string $stepKey - * @param string $type - * @param array $actionAttributes + * @param string $stepKey + * @param string $type + * @param array $actionAttributes * @param string|null $linkedAction - * @param string $order - * @param array $actionOrigin + * @param string $order + * @param array $actionOrigin */ public function __construct( $stepKey, @@ -207,7 +207,7 @@ public function getLinkedAction() /** * This function returns the int property orderOffset, describing before or after for a merge. * - * @return int + * @return integer */ public function getOrderOffset() { @@ -218,7 +218,7 @@ public function getOrderOffset() * This function returns the int property timeout, this can be set as a result of the use of a section element * requiring a wait. * - * @return int + * @return integer */ public function getTimeout() { @@ -228,7 +228,7 @@ public function getTimeout() /** * Set the timeout value. * - * @param int $timeout + * @param integer $timeout * @return void */ public function setTimeout($timeout) @@ -277,7 +277,6 @@ public function trimAssertionAttributes() */ $oldAttributes = array_intersect($actionAttributeKeys, ActionObject::OLD_ASSERTION_ATTRIBUTES); if (!empty($oldAttributes)) { - // @codingStandardsIgnoreStart $appConfig = MftfApplicationConfig::getConfig(); if ($appConfig->getPhase() == MftfApplicationConfig::GENERATION_PHASE && $appConfig->verboseEnabled()) { LoggingUtil::getInstance()->getLogger(ActionObject::class)->warning( @@ -285,7 +284,6 @@ public function trimAssertionAttributes() ["action" => $this->type, "stepKey" => $this->stepKey] ); } - // @codingStandardsIgnoreEnd return; } @@ -329,12 +327,10 @@ private function validateAssertionSchema($attributes) if (!in_array($this->type, $singleChildTypes)) { if (!in_array('expectedResult', $attributes) || !in_array('actualResult', $attributes)) { - // @codingStandardsIgnoreStart throw new TestReferenceException( - "{$this->type} must have both an expectedResult and actualResult defined (stepKey: {$this->stepKey})", + "{$this->type} must have both an expectedResult & actualResult defined (stepKey: {$this->stepKey})", ["action" => $this->type, "stepKey" => $this->stepKey] ); - // @codingStandardsIgnoreEnd } } } @@ -483,9 +479,10 @@ private function stripAndReturnParameters($reference) * Return a string based on a reference to a page, section, or data field (e.g. {{foo.ref}} resolves to 'data') * * @param ObjectHandlerInterface $objectHandler - * @param string $inputString + * @param string $inputString * @return string | null - * @throws TestReferenceException | \Exception + * @throws TestReferenceException + * @throws \Exception * * @SuppressWarnings(PHPMD.CyclomaticComplexity) */ @@ -616,9 +613,9 @@ private function resolveEntityDataObjectReference($obj, $match) /** * Resolves $replacement parameterization with given conditional. * @param boolean $isParameterized - * @param string $replacement - * @param string $match - * @param object $object + * @param string $replacement + * @param string $match + * @param object $object * @return string * @throws \Exception */ @@ -641,7 +638,7 @@ private function resolveParameterization($isParameterized, $replacement, $match, * Parameter list given is also resolved, attempting to match {{data.field}} references. * * @param string $reference - * @param array $parameters + * @param array $parameters * @return string * @throws \Exception */ diff --git a/src/Magento/FunctionalTestingFramework/Test/Objects/TestHookObject.php b/src/Magento/FunctionalTestingFramework/Test/Objects/TestHookObject.php index 678df6b84..379082632 100644 --- a/src/Magento/FunctionalTestingFramework/Test/Objects/TestHookObject.php +++ b/src/Magento/FunctionalTestingFramework/Test/Objects/TestHookObject.php @@ -36,9 +36,8 @@ class TestHookObject private $actions = []; /** - * Array of Hook-defined data. + * Array of Hook-defined data. Deprecated because no usage of property exist. Will be removed next major release. * @var array|null - * @deprecated because no usage of property exist. Will be removed next major release. */ private $customData = []; @@ -46,7 +45,7 @@ class TestHookObject * TestHookObject constructor. * @param string $type * @param string $parentName - * @param array $actions + * @param array $actions */ public function __construct($type, $parentName, $actions) { diff --git a/src/Magento/FunctionalTestingFramework/Test/Objects/TestObject.php b/src/Magento/FunctionalTestingFramework/Test/Objects/TestObject.php index 3569b189a..5ff2c8803 100644 --- a/src/Magento/FunctionalTestingFramework/Test/Objects/TestObject.php +++ b/src/Magento/FunctionalTestingFramework/Test/Objects/TestObject.php @@ -58,26 +58,26 @@ class TestObject /** * String of filename of test * - * @var String + * @var string */ private $filename; /** * String of parent test * - * @var String + * @var string */ private $parentTest; /** * TestObject constructor. * - * @param string $name - * @param ActionObject[] $parsedSteps - * @param array $annotations + * @param string $name + * @param ActionObject[] $parsedSteps + * @param array $annotations * @param TestHookObject[] $hooks - * @param String $filename - * @param String $parentTest + * @param string $filename + * @param string $parentTest */ public function __construct($name, $parsedSteps, $annotations, $hooks, $filename = null, $parentTest = null) { @@ -173,7 +173,7 @@ public function getHooks() /** * Returns the estimated duration of a single test (including before/after actions). * - * @return int + * @return integer */ public function getEstimatedDuration() { @@ -200,7 +200,7 @@ public function getEstimatedDuration() * Function which takes a set of actions and estimates time for completion based on action type. * * @param ActionObject[] $actions - * @return int + * @return integer */ private function calculateWeightedActionTimes($actions) { diff --git a/src/Magento/FunctionalTestingFramework/Test/Util/ActionMergeUtil.php b/src/Magento/FunctionalTestingFramework/Test/Util/ActionMergeUtil.php index b4188abe0..99e575491 100644 --- a/src/Magento/FunctionalTestingFramework/Test/Util/ActionMergeUtil.php +++ b/src/Magento/FunctionalTestingFramework/Test/Util/ActionMergeUtil.php @@ -68,8 +68,8 @@ public function __construct($contextName, $contextType) /** * Method to execute merge of steps and insert wait steps. * - * @param array $parsedSteps - * @param bool $skipActionGroupResolution + * @param array $parsedSteps + * @param boolean $skipActionGroupResolution * @return array * @throws TestReferenceException * @throws XmlException diff --git a/src/Magento/FunctionalTestingFramework/Test/Util/ActionObjectExtractor.php b/src/Magento/FunctionalTestingFramework/Test/Util/ActionObjectExtractor.php index d157ca5d6..7642aec31 100644 --- a/src/Magento/FunctionalTestingFramework/Test/Util/ActionObjectExtractor.php +++ b/src/Magento/FunctionalTestingFramework/Test/Util/ActionObjectExtractor.php @@ -46,7 +46,7 @@ public function __construct() * This method takes an array of test actions read in from a TestHook or Test. The actions are stripped of * irrelevant tags and returned as an array of ActionObjects. * - * @param array $testActions + * @param array $testActions * @param string $testName * @return array * @throws XmlException @@ -115,7 +115,7 @@ public function extractActions($testActions, $testName = null) * Returns an array with keys corresponding to the linked action's stepKey and order. * * @param string $actionName - * @param array $actionData + * @param array $actionData * @return array * @throws XmlException */ @@ -232,7 +232,7 @@ private function extractFieldReferences($actionData, $actionAttributes) /** * Function which validates stepKey references within mergeable actions * - * @param array $stepKeyRefs + * @param array $stepKeyRefs * @param string $testName * @return void * @throws TestReferenceException diff --git a/src/Magento/FunctionalTestingFramework/Test/Util/AnnotationExtractor.php b/src/Magento/FunctionalTestingFramework/Test/Util/AnnotationExtractor.php index 1718bf059..c8ea53819 100644 --- a/src/Magento/FunctionalTestingFramework/Test/Util/AnnotationExtractor.php +++ b/src/Magento/FunctionalTestingFramework/Test/Util/AnnotationExtractor.php @@ -41,7 +41,7 @@ public function __construct() * This method trims away irrelevant tags and returns annotations used in the array passed. The annotations * can be found in both Tests and their child element tests. * - * @param array $testAnnotations + * @param array $testAnnotations * @param string $filename * @return array */ @@ -73,7 +73,7 @@ public function extractAnnotations($testAnnotations, $filename) /** * Adds story/title/filename combination to static map - * @param array $annotations + * @param array $annotations * @param string $filename * @return void */ @@ -107,7 +107,6 @@ public function validateStoryTitleUniqueness() $story = $storyTitleArray[0]; $title = $storyTitleArray[1]; $message .= "Story: '{$story}' Title: '{$title}' in Tests {$tests}\n\n"; - } throw new XmlException($message); } diff --git a/src/Magento/FunctionalTestingFramework/Test/Util/BaseObjectExtractor.php b/src/Magento/FunctionalTestingFramework/Test/Util/BaseObjectExtractor.php index 50dffc3c3..26644937d 100644 --- a/src/Magento/FunctionalTestingFramework/Test/Util/BaseObjectExtractor.php +++ b/src/Magento/FunctionalTestingFramework/Test/Util/BaseObjectExtractor.php @@ -22,13 +22,12 @@ public function __construct() // empty } - // @codingStandardsIgnoreStart /** * This method takes an array of data and an array representing irrelevant tags. The method strips * the data passed in of the irrelevant tags and returns the result. * * @param array $data - * @param array $tags + * @param array ...$tags * @return array */ protected function stripDescriptorTags($data, ...$tags) @@ -40,5 +39,4 @@ protected function stripDescriptorTags($data, ...$tags) return $results; } - // @codingStandardsIgnoreEnd } diff --git a/src/Magento/FunctionalTestingFramework/Test/Util/TestHookObjectExtractor.php b/src/Magento/FunctionalTestingFramework/Test/Util/TestHookObjectExtractor.php index 0084800b5..c11a6e512 100644 --- a/src/Magento/FunctionalTestingFramework/Test/Util/TestHookObjectExtractor.php +++ b/src/Magento/FunctionalTestingFramework/Test/Util/TestHookObjectExtractor.php @@ -35,7 +35,7 @@ public function __construct() * * @param string $parentName * @param string $hookType - * @param array $testHook + * @param array $testHook * @return TestHookObject * @throws \Exception */ diff --git a/src/Magento/FunctionalTestingFramework/Util/ConfigSanitizerUtil.php b/src/Magento/FunctionalTestingFramework/Util/ConfigSanitizerUtil.php index 802db4127..ce5740493 100644 --- a/src/Magento/FunctionalTestingFramework/Util/ConfigSanitizerUtil.php +++ b/src/Magento/FunctionalTestingFramework/Util/ConfigSanitizerUtil.php @@ -15,7 +15,7 @@ class ConfigSanitizerUtil { /** * Sanitizes the given Webdriver Config's url and selenium env params, can be selective based on second argument. - * @param array $config + * @param array $config * @param String[] $params * @return array */ diff --git a/src/Magento/FunctionalTestingFramework/Util/Env/EnvProcessor.php b/src/Magento/FunctionalTestingFramework/Util/Env/EnvProcessor.php index a954e0681..f09ab63fa 100644 --- a/src/Magento/FunctionalTestingFramework/Util/Env/EnvProcessor.php +++ b/src/Magento/FunctionalTestingFramework/Util/Env/EnvProcessor.php @@ -1,5 +1,4 @@ <?php -// @codingStandardsIgnoreFile /** * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. @@ -39,7 +38,7 @@ class EnvProcessor /** * Boolean indicating existence of env file * - * @var bool + * @var boolean */ private $envExists; @@ -80,6 +79,11 @@ private function parseEnvFile(): array return array_merge($this->parseEnvFileLines($envExampleFile), $envContents); } + /** + * Iterates through env and returns array of file contents. + * @param array $file + * @return array + */ private function parseEnvFileLines(array $file): array { $fileArray = []; diff --git a/src/Magento/FunctionalTestingFramework/Util/Iterator/AbstractIterator.php b/src/Magento/FunctionalTestingFramework/Util/Iterator/AbstractIterator.php index 0b95a4ca7..69e2527aa 100644 --- a/src/Magento/FunctionalTestingFramework/Util/Iterator/AbstractIterator.php +++ b/src/Magento/FunctionalTestingFramework/Util/Iterator/AbstractIterator.php @@ -99,7 +99,7 @@ public function valid() /** * Get data key of the current data element * - * @return int|string + * @return integer|string */ public function key() { @@ -109,7 +109,7 @@ public function key() /** * To make iterator countable * - * @return int + * @return integer */ public function count() { diff --git a/src/Magento/FunctionalTestingFramework/Util/Manifest/BaseTestManifest.php b/src/Magento/FunctionalTestingFramework/Util/Manifest/BaseTestManifest.php index e5ce1ee50..f7ad8aea2 100644 --- a/src/Magento/FunctionalTestingFramework/Util/Manifest/BaseTestManifest.php +++ b/src/Magento/FunctionalTestingFramework/Util/Manifest/BaseTestManifest.php @@ -38,7 +38,7 @@ abstract class BaseTestManifest * * @param string $path * @param string $runConfig - * @param array $suiteConfiguration + * @param array $suiteConfiguration */ public function __construct($path, $runConfig, $suiteConfiguration) { diff --git a/src/Magento/FunctionalTestingFramework/Util/Manifest/DefaultTestManifest.php b/src/Magento/FunctionalTestingFramework/Util/Manifest/DefaultTestManifest.php index f6af0fe88..eb4f79db2 100644 --- a/src/Magento/FunctionalTestingFramework/Util/Manifest/DefaultTestManifest.php +++ b/src/Magento/FunctionalTestingFramework/Util/Manifest/DefaultTestManifest.php @@ -37,7 +37,7 @@ class DefaultTestManifest extends BaseTestManifest /** * DefaultTestManifest constructor. * - * @param array $suiteConfiguration + * @param array $suiteConfiguration * @param string $testPath */ public function __construct($suiteConfiguration, $testPath) diff --git a/src/Magento/FunctionalTestingFramework/Util/Manifest/ParallelTestManifest.php b/src/Magento/FunctionalTestingFramework/Util/Manifest/ParallelTestManifest.php index c60634bd3..9b12cfd00 100644 --- a/src/Magento/FunctionalTestingFramework/Util/Manifest/ParallelTestManifest.php +++ b/src/Magento/FunctionalTestingFramework/Util/Manifest/ParallelTestManifest.php @@ -52,7 +52,7 @@ class ParallelTestManifest extends BaseTestManifest /** * TestManifest constructor. * - * @param array $suiteConfiguration + * @param array $suiteConfiguration * @param string $testPath */ public function __construct($suiteConfiguration, $testPath) @@ -77,7 +77,7 @@ public function addTest($testObject) * Function which generates test groups based on arg passed. The function builds groups using the args as an upper * limit. * - * @param int $time + * @param integer $time * @return void */ public function createTestGroups($time) @@ -121,9 +121,9 @@ public function getSorter() * for the entry in order to generate a txt file used by devops for parllel execution in Jenkins. The results * are checked against a flattened list of suites in order to generate proper entries. * - * @param array $testGroup - * @param int $nodeNumber - * @param array $suites + * @param array $testGroup + * @param integer $nodeNumber + * @param array $suites * @return void */ private function generateGroupFile($testGroup, $nodeNumber, $suites) diff --git a/src/Magento/FunctionalTestingFramework/Util/Manifest/SingleRunTestManifest.php b/src/Magento/FunctionalTestingFramework/Util/Manifest/SingleRunTestManifest.php index 94004e3be..417fca686 100644 --- a/src/Magento/FunctionalTestingFramework/Util/Manifest/SingleRunTestManifest.php +++ b/src/Magento/FunctionalTestingFramework/Util/Manifest/SingleRunTestManifest.php @@ -13,7 +13,7 @@ class SingleRunTestManifest extends DefaultTestManifest /** * SingleRunTestManifest constructor. * - * @param array $suiteConfiguration + * @param array $suiteConfiguration * @param string $testPath */ public function __construct($suiteConfiguration, $testPath) diff --git a/src/Magento/FunctionalTestingFramework/Util/Manifest/TestManifestFactory.php b/src/Magento/FunctionalTestingFramework/Util/Manifest/TestManifestFactory.php index 51b34bc80..1cfa6559e 100644 --- a/src/Magento/FunctionalTestingFramework/Util/Manifest/TestManifestFactory.php +++ b/src/Magento/FunctionalTestingFramework/Util/Manifest/TestManifestFactory.php @@ -22,9 +22,9 @@ private function __construct() /** * Static function which takes path and config to return the appropriate manifest output type. * - * @param String $runConfig - * @param array $suiteConfiguration - * @param String $testPath + * @param string $runConfig + * @param array $suiteConfiguration + * @param string $testPath * @return BaseTestManifest */ public static function makeManifest($runConfig, $suiteConfiguration, $testPath = TestGenerator::DEFAULT_DIR) @@ -44,7 +44,6 @@ public static function makeManifest($runConfig, $suiteConfiguration, $testPath = default: return new DefaultTestManifest($suiteConfiguration, $testDirFullPath); - } } } diff --git a/src/Magento/FunctionalTestingFramework/Util/MetadataGenerator/FormData/MetadataGenUtil.php b/src/Magento/FunctionalTestingFramework/Util/MetadataGenerator/FormData/MetadataGenUtil.php index 821f598a7..2cd3f7011 100644 --- a/src/Magento/FunctionalTestingFramework/Util/MetadataGenerator/FormData/MetadataGenUtil.php +++ b/src/Magento/FunctionalTestingFramework/Util/MetadataGenerator/FormData/MetadataGenUtil.php @@ -123,7 +123,7 @@ private function appendParentParams($data) * Function which is called recursively to generate the mustache array for the template enging. Makes decisions * about type and format based on parameter array. * - * @param array $results + * @param array $results * @param string $defaultDataType * @return array */ diff --git a/src/Magento/FunctionalTestingFramework/Util/MetadataGenerator/Swagger/MetadataGenerator.php b/src/Magento/FunctionalTestingFramework/Util/MetadataGenerator/Swagger/MetadataGenerator.php index 64c981fe1..10d75bf8e 100644 --- a/src/Magento/FunctionalTestingFramework/Util/MetadataGenerator/Swagger/MetadataGenerator.php +++ b/src/Magento/FunctionalTestingFramework/Util/MetadataGenerator/Swagger/MetadataGenerator.php @@ -140,8 +140,8 @@ public function generateMetadataFromSwagger() * Render swagger operations. * * @param Operation $operation - * @param string $path - * @param string $method + * @param string $path + * @param string $method * @return void */ private function renderOperation($operation, $path, $method) @@ -182,7 +182,7 @@ private function renderOperation($operation, $path, $method) /** * Render swagger definitions. * - * @param string $defKey + * @param string $defKey * @param ObjectSchema|ArraySchema $definition * @return void */ @@ -232,9 +232,9 @@ private function renderDefinition($defKey, $definition) * Parse schema and return an array that will be consumed by mustache template engine. * * @param SchemaInterface $schema - * @param string $name - * @param bool $forArray - * @param integer $depth + * @param string $name + * @param boolean $forArray + * @param integer $depth * @return array */ private function parseSchema($schema, $name, $forArray, $depth) @@ -285,7 +285,7 @@ private function parseSchema($schema, $name, $forArray, $depth) * Parse params for an operation. * * @param ArrayCollection $params - * @param string $path + * @param string $path * @return void */ private function parseParams($params, $path) @@ -348,7 +348,7 @@ private function setBodyParams($param) * Set path params for an operation. * * @param AbstractTypedParameter $param - * @param string $path + * @param string $path * @return void */ private function setPathParams($param, $path) @@ -406,7 +406,7 @@ private function initMustacheTemplates() * @param string $relativeDir * @param string $fileName * @param string $template - * @param array $data + * @param array $data * @return void */ private function generateMetaDataFile($relativeDir, $fileName, $template, $data) diff --git a/src/Magento/FunctionalTestingFramework/Util/ModuleResolver.php b/src/Magento/FunctionalTestingFramework/Util/ModuleResolver.php index 1b9f3ce2b..363d3b9e6 100644 --- a/src/Magento/FunctionalTestingFramework/Util/ModuleResolver.php +++ b/src/Magento/FunctionalTestingFramework/Util/ModuleResolver.php @@ -368,7 +368,7 @@ private function printMagentoVersionInfo() /** * Get the API token for admin. * - * @return string|bool + * @return string|boolean */ protected function getAdminToken() { diff --git a/src/Magento/FunctionalTestingFramework/Util/Protocol/CurlInterface.php b/src/Magento/FunctionalTestingFramework/Util/Protocol/CurlInterface.php index 06d843fea..171fd01e6 100644 --- a/src/Magento/FunctionalTestingFramework/Util/Protocol/CurlInterface.php +++ b/src/Magento/FunctionalTestingFramework/Util/Protocol/CurlInterface.php @@ -22,8 +22,8 @@ interface CurlInterface /** * Add additional option to cURL. * - * @param int $option - * @param int|string|bool|array $value + * @param integer $option + * @param integer|string|boolean|array $value * @return $this */ public function addOption($option, $value); @@ -31,10 +31,10 @@ public function addOption($option, $value); /** * Send request to the remote server. * - * @param string $url + * @param string $url * @param array|string $body - * @param string $method - * @param array $headers + * @param string $method + * @param array $headers * @return void */ public function write($url, $body = [], $method = CurlInterface::POST, $headers = []); diff --git a/src/Magento/FunctionalTestingFramework/Util/Protocol/CurlTransport.php b/src/Magento/FunctionalTestingFramework/Util/Protocol/CurlTransport.php index 4372efadd..5a9133a05 100644 --- a/src/Magento/FunctionalTestingFramework/Util/Protocol/CurlTransport.php +++ b/src/Magento/FunctionalTestingFramework/Util/Protocol/CurlTransport.php @@ -92,8 +92,8 @@ public function setOptions(array $options = []) /** * Add additional option to cURL. * - * @param int $option - * @param int|string|bool|array $value + * @param integer $option + * @param integer|string|boolean|array $value * @return $this */ public function addOption($option, $value) @@ -117,10 +117,10 @@ public function setConfig(array $config = []) /** * Send request to the remote server. * - * @param string $url + * @param string $url * @param array|string $body - * @param string $method - * @param array $headers + * @param string $method + * @param array $headers * @return void * @throws TestFrameworkException */ @@ -208,7 +208,7 @@ protected function getResource() /** * Get last error number. * - * @return int + * @return integer */ public function getErrno() { @@ -228,7 +228,7 @@ public function getError() /** * Get information regarding a specific transfer. * - * @param int $opt CURLINFO option + * @param integer $opt CURLINFO option. * @return string|array */ public function getInfo($opt = 0) @@ -278,7 +278,7 @@ public function multiRequest(array $urls, array $options = []) * Extract the response code from a response string. * * @param string $responseStr - * @return int + * @return integer */ public static function extractCode($responseStr) { diff --git a/src/Magento/FunctionalTestingFramework/Util/Sorter/ParallelGroupSorter.php b/src/Magento/FunctionalTestingFramework/Util/Sorter/ParallelGroupSorter.php index eff9c6d8e..8654d64a7 100644 --- a/src/Magento/FunctionalTestingFramework/Util/Sorter/ParallelGroupSorter.php +++ b/src/Magento/FunctionalTestingFramework/Util/Sorter/ParallelGroupSorter.php @@ -28,8 +28,8 @@ public function __construct() /** * Function which returns tests and suites split according to desired number of lines divded into groups. * - * @param array $suiteConfiguration - * @param array $testNameToSize + * @param array $suiteConfiguration + * @param array $testNameToSize * @param integer $time * @return array * @throws TestFrameworkException @@ -89,9 +89,9 @@ public function getResultingSuiteConfig() * the group. * * @param integer $timeMaximum - * @param string $testName + * @param string $testName * @param integer $testSize - * @param array $testNameToSizeForUse + * @param array $testNameToSizeForUse * @return array */ private function createTestGroup($timeMaximum, $testName, $testSize, $testNameToSizeForUse) @@ -110,7 +110,6 @@ private function createTestGroup($timeMaximum, $testName, $testSize, $testNameTo } unset($testNameToSizeForUse[$testNameForUse]); - } } @@ -121,7 +120,7 @@ private function createTestGroup($timeMaximum, $testName, $testSize, $testNameTo * Function which takes a group of available tests mapped to size and a desired number of lines matching with the * test of closest size and returning. * - * @param array $testGroup + * @param array $testGroup * @param integer $desiredValue * @return string */ @@ -148,7 +147,7 @@ private function getClosestLineCount($testGroup, $desiredValue) * Function which takes an array of test names mapped to suite name and a size limitation for each group of tests. * The function divides suites that are over the specified limit and returns the resulting suites in an array. * - * @param array $suiteConfiguration + * @param array $suiteConfiguration * @param integer $lineLimit * @return array */ @@ -229,8 +228,8 @@ private function getSuiteToSize($suiteNamesToTests) * Input {suitename = 'sample', tests = ['test1' => 100,'test2' => 150, 'test3' => 300], linelimit = 275} * Result { ['sample_01' => ['test3' => 300], 'sample_02' => ['test2' => 150, 'test1' => 100]] } * - * @param string $suiteName - * @param array $tests + * @param string $suiteName + * @param array $tests * @param integer $maxTime * @return array */ @@ -264,7 +263,7 @@ private function splitTestSuite($suiteName, $tests, $maxTime) * * @param string $originalSuiteName * @param string $newSuiteName - * @param array $tests + * @param array $tests * @return void */ private function addSuiteToConfig($originalSuiteName, $newSuiteName, $tests) diff --git a/src/Magento/FunctionalTestingFramework/Util/TestGenerator.php b/src/Magento/FunctionalTestingFramework/Util/TestGenerator.php index 022ccefc4..f34326f9b 100644 --- a/src/Magento/FunctionalTestingFramework/Util/TestGenerator.php +++ b/src/Magento/FunctionalTestingFramework/Util/TestGenerator.php @@ -65,16 +65,16 @@ class TestGenerator /** * Debug flag. * - * @var bool + * @var boolean */ private $debug; /** * TestGenerator constructor. * - * @param string $exportDir - * @param array $tests - * @param bool $debug + * @param string $exportDir + * @param array $tests + * @param boolean $debug */ private function __construct($exportDir, $tests, $debug = false) { @@ -94,9 +94,9 @@ private function __construct($exportDir, $tests, $debug = false) /** * Singleton method to retrieve Test Generator * - * @param string $dir - * @param array $tests - * @param bool $debug + * @param string $dir + * @param array $tests + * @param boolean $debug * @return TestGenerator */ public static function getInstance($dir = null, $tests = [], $debug = false) @@ -168,7 +168,7 @@ private function createCestFile($testPhp, $filename) * to the createCestFile function. * * @param BaseTestManifest $testManifest - * @param array $testsToIgnore + * @param array $testsToIgnore * @return void * @throws TestReferenceException * @throws \Exception @@ -230,7 +230,7 @@ private function assembleTestPhp($testObject) * Load ALL Test objects. Loop over and pass each to the assembleTestPhp function. * * @param BaseTestManifest $testManifest - * @param array $testsToIgnore + * @param array $testsToIgnore * @return array */ private function assembleAllTestPhp($testManifest, array $testsToIgnore) @@ -310,7 +310,7 @@ private function generateUseStatementsPhp() /** * Generates Annotations PHP for given object, using given scope to determine indentation and additional output. * - * @param array $annotationsObject + * @param array $annotationsObject * @param boolean $isMethod * @return string */ @@ -349,7 +349,7 @@ private function generateAnnotationsPhp($annotationsObject, $isMethod = false) /** * Method which returns formatted method level annotation based on type and name(s). * - * @param string $annotationType + * @param string $annotationType * @param string|null $annotationName * @return null|string */ @@ -415,7 +415,6 @@ private function generateClassAnnotations($annotationType, $annotationName) $annotationToAppend = null; switch ($annotationType) { - case "title": $annotationToAppend = sprintf(" * @Title(\"%s\")\n", $annotationName[0]); break; @@ -448,9 +447,9 @@ private function generateClassAnnotations($annotationType, $annotationName) * statement to handle each unique action. At the bottom of the case statement there is a generic function that can * construct the PHP string for nearly half of all Codeception actions. * - * @param array $actionObjects - * @param array|bool $hookObject - * @param string $actor + * @param array $actionObjects + * @param array|boolean $hookObject + * @param string $actor * @return string * @throws TestReferenceException * @throws \Exception @@ -1312,8 +1311,8 @@ private function trimVariableIfNeeded($input) /** * Replaces all matches into given outputArg with. Variable scope determined by delimiter given. * - * @param array $matches - * @param string &$outputArg + * @param array $matches + * @param string $outputArg * @param string $delimiter * @return void * @throws \Exception @@ -1370,7 +1369,7 @@ private function processQuoteBreaks($match, $argument, $replacement) * Replaces any occurrences of stepKeys in input, if they are found within the given actionGroup. * Necessary to allow for use of grab/createData actions in actionGroups. * @param string $input - * @param array $actionGroupOrigin + * @param array $actionGroupOrigin * @return string */ private function resolveStepKeyReferences($input, $actionGroupOrigin) @@ -1398,7 +1397,6 @@ private function resolveStepKeyReferences($input, $actionGroupOrigin) if (strpos($output, $classVarRef) !== false) { $output = str_replace($classVarRef, $classVarRef . $testInvocationKey, $output); } - } return $output; } @@ -1717,7 +1715,7 @@ private function wrapFunctionCallWithReturnValue($returnVariable, $actor, $actio /** * Resolves {{_ENV.variable}} into getenv("variable") for test-runtime ENV referencing. - * @param array $args + * @param array $args * @param string $regex * @param string $func * @return array @@ -1819,7 +1817,7 @@ private function resolveValueByType($value, $type) * Convert input string to boolean equivalent. * * @param string $inStr - * @return bool|null + * @return boolean|null */ private function toBoolean($inStr) { @@ -1830,7 +1828,7 @@ private function toBoolean($inStr) * Convert input string to number equivalent. * * @param string $inStr - * @return int|float|null + * @return integer|float|null */ private function toNumber($inStr) { @@ -1859,7 +1857,7 @@ private function stripQuotes($inStr) * * @param string $key * @param string $tagName - * @param array $attributes + * @param array $attributes * @return void */ private function validateXmlAttributesMutuallyExclusive($key, $tagName, $attributes) @@ -1918,7 +1916,7 @@ private function validateXmlAttributesMutuallyExclusive($key, $tagName, $attribu * * @param string $key * @param string $tagName - * @param array $attributes + * @param array $attributes * @return void */ private function printRuleErrorToConsole($key, $tagName, $attributes) diff --git a/src/Magento/FunctionalTestingFramework/Util/Validation/DuplicateNodeValidationUtil.php b/src/Magento/FunctionalTestingFramework/Util/Validation/DuplicateNodeValidationUtil.php index d0352d944..8508f1abe 100644 --- a/src/Magento/FunctionalTestingFramework/Util/Validation/DuplicateNodeValidationUtil.php +++ b/src/Magento/FunctionalTestingFramework/Util/Validation/DuplicateNodeValidationUtil.php @@ -29,7 +29,7 @@ class DuplicateNodeValidationUtil /** * DuplicateNodeValidationUtil constructor. - * @param string $uniqueKey + * @param string $uniqueKey * @param ExceptionCollector $exceptionCollector */ public function __construct($uniqueKey, $exceptionCollector) @@ -42,7 +42,7 @@ public function __construct($uniqueKey, $exceptionCollector) * Parses through parent's children to find and flag duplicate values in given uniqueKey. * * @param \DOMElement $parentNode - * @param string $filename + * @param string $filename * @return void */ public function validateChildUniqueness(\DOMElement $parentNode, $filename) diff --git a/src/Magento/FunctionalTestingFramework/XmlParser/PageParser.php b/src/Magento/FunctionalTestingFramework/XmlParser/PageParser.php index 0070dc08f..643370504 100644 --- a/src/Magento/FunctionalTestingFramework/XmlParser/PageParser.php +++ b/src/Magento/FunctionalTestingFramework/XmlParser/PageParser.php @@ -30,7 +30,7 @@ class PageParser implements ParserInterface /** * PageParser Constructor * @param ObjectManagerInterface $objectManager - * @param DataInterface $configData + * @param DataInterface $configData */ public function __construct(ObjectManagerInterface $objectManager, DataInterface $configData) { diff --git a/src/Magento/FunctionalTestingFramework/XmlParser/SectionParser.php b/src/Magento/FunctionalTestingFramework/XmlParser/SectionParser.php index 4a77b97b2..ff8f92c42 100644 --- a/src/Magento/FunctionalTestingFramework/XmlParser/SectionParser.php +++ b/src/Magento/FunctionalTestingFramework/XmlParser/SectionParser.php @@ -31,7 +31,7 @@ class SectionParser implements ParserInterface /** * SectionParser constructor. * @param ObjectManagerInterface $objectManager - * @param DataInterface $configData + * @param DataInterface $configData */ public function __construct(ObjectManagerInterface $objectManager, DataInterface $configData) { From 9737bd8bb6d4d5be699a5c5dd888a157663bce0a Mon Sep 17 00:00:00 2001 From: Alex Calandra <acalandra@magento.com> Date: Tue, 26 Jun 2018 15:29:24 -0500 Subject: [PATCH 078/111] MQE-1068: Require Issue ID for Skipped Test - Removed IssueId comment generator - Updated Tests - Added merge test --- .../verification/Resources/MergeSkip.txt | 34 +++++++++++++++++++ .../verification/Resources/SkippedTest.txt | 1 - .../Resources/SkippedTestTwoIssues.txt | 2 -- .../TestModule/Test/MergeFunctionalTest.xml | 3 ++ .../Test/MergeFunctionalTest.xml | 7 ++++ .../Tests/MergedGenerationTest.php | 11 ++++++ .../Util/TestGenerator.php | 6 ---- 7 files changed, 55 insertions(+), 9 deletions(-) create mode 100644 dev/tests/verification/Resources/MergeSkip.txt diff --git a/dev/tests/verification/Resources/MergeSkip.txt b/dev/tests/verification/Resources/MergeSkip.txt new file mode 100644 index 000000000..180c640fe --- /dev/null +++ b/dev/tests/verification/Resources/MergeSkip.txt @@ -0,0 +1,34 @@ +<?php +namespace Magento\AcceptanceTest\_default\Backend; + +use Magento\FunctionalTestingFramework\AcceptanceTester; +use Magento\FunctionalTestingFramework\DataGenerator\Handlers\DataObjectHandler; +use Magento\FunctionalTestingFramework\DataGenerator\Persist\DataPersistenceHandler; +use Magento\FunctionalTestingFramework\DataGenerator\Objects\EntityDataObject; +use Magento\FunctionalTestingFramework\DataGenerator\Handlers\CredentialStore; +use \Codeception\Util\Locator; +use Yandex\Allure\Adapter\Annotation\Features; +use Yandex\Allure\Adapter\Annotation\Stories; +use Yandex\Allure\Adapter\Annotation\Title; +use Yandex\Allure\Adapter\Annotation\Description; +use Yandex\Allure\Adapter\Annotation\Parameter; +use Yandex\Allure\Adapter\Annotation\Severity; +use Yandex\Allure\Adapter\Model\SeverityLevel; +use Yandex\Allure\Adapter\Annotation\TestCaseId; + +/** + */ +class MergeSkipCest +{ + /** + * @Features({"TestModule"}) + * @Parameter(name = "AcceptanceTester", value="$I") + * @param AcceptanceTester $I + * @return void + * @throws \Exception + */ + public function MergeSkip(AcceptanceTester $I, \Codeception\Scenario $scenario) + { + $scenario->skip("This test is skipped"); + } +} diff --git a/dev/tests/verification/Resources/SkippedTest.txt b/dev/tests/verification/Resources/SkippedTest.txt index 708d93ef6..2b519cb42 100644 --- a/dev/tests/verification/Resources/SkippedTest.txt +++ b/dev/tests/verification/Resources/SkippedTest.txt @@ -19,7 +19,6 @@ use Yandex\Allure\Adapter\Annotation\TestCaseId; /** * @Title("skippedTest") * @Description("") - * @skipIssueId SkippedValue */ class SkippedTestCest { diff --git a/dev/tests/verification/Resources/SkippedTestTwoIssues.txt b/dev/tests/verification/Resources/SkippedTestTwoIssues.txt index 18c72c03e..48f473e7e 100644 --- a/dev/tests/verification/Resources/SkippedTestTwoIssues.txt +++ b/dev/tests/verification/Resources/SkippedTestTwoIssues.txt @@ -19,8 +19,6 @@ use Yandex\Allure\Adapter\Annotation\TestCaseId; /** * @Title("skippedMultipleIssuesTest") * @Description("") - * @skipIssueId SkippedValue - * @skipIssueId SecondSkippedValue */ class SkippedTestTwoIssuesCest { diff --git a/dev/tests/verification/TestModule/Test/MergeFunctionalTest.xml b/dev/tests/verification/TestModule/Test/MergeFunctionalTest.xml index f7997946c..5189b8cf4 100644 --- a/dev/tests/verification/TestModule/Test/MergeFunctionalTest.xml +++ b/dev/tests/verification/TestModule/Test/MergeFunctionalTest.xml @@ -55,4 +55,7 @@ <click stepKey="clickTwo" selector="#mergeTwo"/> <click stepKey="clickThree" selector="#mergeThree"/> </test> + <test name="MergeSkip"> + <comment userInput="ThisTestShouldBeSkipped" stepKey="skipComment"/> + </test> </tests> diff --git a/dev/tests/verification/TestModuleMerged/Test/MergeFunctionalTest.xml b/dev/tests/verification/TestModuleMerged/Test/MergeFunctionalTest.xml index aa8cb375d..912854cc4 100644 --- a/dev/tests/verification/TestModuleMerged/Test/MergeFunctionalTest.xml +++ b/dev/tests/verification/TestModuleMerged/Test/MergeFunctionalTest.xml @@ -26,4 +26,11 @@ <click stepKey="step10" selector="#step10MergedInResult"/> <actionGroup ref="FunctionalActionGroupWithData" stepKey="step8Merge" after="step7Merge"/> </test> + <test name="MergeSkip"> + <annotations> + <skip> + <issueId value="Issue5"/> + </skip> + </annotations> + </test> </tests> diff --git a/dev/tests/verification/Tests/MergedGenerationTest.php b/dev/tests/verification/Tests/MergedGenerationTest.php index 9ef22fe09..c336cd8d8 100644 --- a/dev/tests/verification/Tests/MergedGenerationTest.php +++ b/dev/tests/verification/Tests/MergedGenerationTest.php @@ -62,4 +62,15 @@ public function testMergeMassViaInsertAfter() { $this->generateAndCompareTest('MergeMassViaInsertAfter'); } + + /** + * Tests generation of a test skipped in merge. + * + * @throws \Exception + * @throws \Magento\FunctionalTestingFramework\Exceptions\TestReferenceException + */ + public function testMergeSkipGeneration() + { + $this->generateAndCompareTest('MergeSkip'); + } } diff --git a/src/Magento/FunctionalTestingFramework/Util/TestGenerator.php b/src/Magento/FunctionalTestingFramework/Util/TestGenerator.php index ace2380de..022ccefc4 100644 --- a/src/Magento/FunctionalTestingFramework/Util/TestGenerator.php +++ b/src/Magento/FunctionalTestingFramework/Util/TestGenerator.php @@ -437,12 +437,6 @@ private function generateClassAnnotations($annotationType, $annotationName) $annotationToAppend .= sprintf(" * @group %s\n", $group); } break; - - case "skip": - foreach ($annotationName as $issue) { - $annotationToAppend .= sprintf(" * @skipIssueId %s\n", $issue); - } - break; } return $annotationToAppend; From e7833c7b02daffc4d830040bf7aeef90760e6169 Mon Sep 17 00:00:00 2001 From: Alex Calandra <acalandra@magento.com> Date: Wed, 27 Jun 2018 08:52:47 -0500 Subject: [PATCH 079/111] MQE-1068: Require Issue ID for Skipped Test - Changed to log for warning --- .../Test/Util/AnnotationExtractor.php | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/Magento/FunctionalTestingFramework/Test/Util/AnnotationExtractor.php b/src/Magento/FunctionalTestingFramework/Test/Util/AnnotationExtractor.php index 01ea44f0d..9b73deef4 100644 --- a/src/Magento/FunctionalTestingFramework/Test/Util/AnnotationExtractor.php +++ b/src/Magento/FunctionalTestingFramework/Test/Util/AnnotationExtractor.php @@ -6,7 +6,9 @@ namespace Magento\FunctionalTestingFramework\Test\Util; +use Magento\FunctionalTestingFramework\Config\MftfApplicationConfig; use Magento\FunctionalTestingFramework\Exceptions\XmlException; +use Magento\FunctionalTestingFramework\Util\Logger\LoggingUtil; /** * Class AnnotationExtractor @@ -73,12 +75,12 @@ public function extractAnnotations($testAnnotations, $filename) } // TODO deprecation|deprecate MFTF 3.0.0 if ($annotationKey == "group" && in_array("skip", $annotationValues)) { - print( - "Test: $filename" . - PHP_EOL . - "Use of group skip will be deprecated in MFTF 3.0.0. Please update tests to use skip tags." . - PHP_EOL - ); + if (MftfApplicationConfig::getConfig()->verboseEnabled()) { + LoggingUtil::getInstance()->getLogger(AnnotationExtractor::class)->warning( + "Use of group skip will be deprecated in MFTF 3.0.0. Please update tests to use skip tags.", + ["test" => $filename] + ); + } } $annotationObjects[$annotationKey] = $annotationValues; From 2cfa59a04bb12d267c4afd912e670f1aa1408a37 Mon Sep 17 00:00:00 2001 From: Alex Calandra <acalandra@magento.com> Date: Wed, 27 Jun 2018 09:53:40 -0500 Subject: [PATCH 080/111] MQE-1068: Require Issue ID for Skipped Test - Removed verbose requirement for deprecation warnign flag --- .../Test/Util/AnnotationExtractor.php | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/Magento/FunctionalTestingFramework/Test/Util/AnnotationExtractor.php b/src/Magento/FunctionalTestingFramework/Test/Util/AnnotationExtractor.php index 9b73deef4..8d93e379d 100644 --- a/src/Magento/FunctionalTestingFramework/Test/Util/AnnotationExtractor.php +++ b/src/Magento/FunctionalTestingFramework/Test/Util/AnnotationExtractor.php @@ -75,12 +75,10 @@ public function extractAnnotations($testAnnotations, $filename) } // TODO deprecation|deprecate MFTF 3.0.0 if ($annotationKey == "group" && in_array("skip", $annotationValues)) { - if (MftfApplicationConfig::getConfig()->verboseEnabled()) { - LoggingUtil::getInstance()->getLogger(AnnotationExtractor::class)->warning( - "Use of group skip will be deprecated in MFTF 3.0.0. Please update tests to use skip tags.", - ["test" => $filename] - ); - } + LoggingUtil::getInstance()->getLogger(AnnotationExtractor::class)->warning( + "Use of group skip will be deprecated in MFTF 3.0.0. Please update tests to use skip tags.", + ["test" => $filename] + ); } $annotationObjects[$annotationKey] = $annotationValues; From 7b54e5ac809b6e9ddec5dd5e776a1baeb951b2fa Mon Sep 17 00:00:00 2001 From: Alex Calandra <acalandra@magento.com> Date: Wed, 27 Jun 2018 10:33:15 -0500 Subject: [PATCH 081/111] MQE-1074: Verify MFTF Compatibility with Phpunit 7 - Adding support for phpunit 6 or 7 --- composer.json | 4 ++-- composer.lock | 12 ++++++------ 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/composer.json b/composer.json index 2ec1c32e4..b3c94d9b5 100755 --- a/composer.json +++ b/composer.json @@ -11,7 +11,7 @@ "require": { "php": "7.0.2|7.0.4|~7.0.6|~7.1.0|~7.2.0", "allure-framework/allure-codeception": "~1.2.6", - "codeception/codeception": "~2.4.2", + "codeception/codeception": "~2.3.4|~2.4.2", "consolidation/robo": "^1.0.0", "epfremme/swagger-php": "^2.0", "flow/jsonpath": ">0.2", @@ -23,7 +23,7 @@ }, "require-dev": { "squizlabs/php_codesniffer": "1.5.3", - "sebastian/phpcpd": "~4.0", + "sebastian/phpcpd": "~3.0|~4.0", "brainmaestro/composer-git-hooks": "^2.3", "doctrine/cache": "<1.7.0", "codeception/aspect-mock": "^3.0", diff --git a/composer.lock b/composer.lock index c576be44c..89870ae17 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "a572ba34cb4bddd81eaa67c23ad3e9a8", + "content-hash": "fbcb8c1052351276ad267a18ee187b01", "packages": [ { "name": "allure-framework/allure-codeception", @@ -170,16 +170,16 @@ }, { "name": "codeception/codeception", - "version": "2.4.2", + "version": "2.4.3", "source": { "type": "git", "url": "https://github.com/Codeception/Codeception.git", - "reference": "bc8433e3da75bc28c47df6f3afc22b9d4f65c0b4" + "reference": "13b2db0d54068afaabf3ca8ac8b6591d69018f46" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Codeception/Codeception/zipball/bc8433e3da75bc28c47df6f3afc22b9d4f65c0b4", - "reference": "bc8433e3da75bc28c47df6f3afc22b9d4f65c0b4", + "url": "https://api.github.com/repos/Codeception/Codeception/zipball/13b2db0d54068afaabf3ca8ac8b6591d69018f46", + "reference": "13b2db0d54068afaabf3ca8ac8b6591d69018f46", "shasum": "" }, "require": { @@ -257,7 +257,7 @@ "functional testing", "unit testing" ], - "time": "2018-05-26T22:17:46+00:00" + "time": "2018-06-26T14:09:28+00:00" }, { "name": "codeception/phpunit-wrapper", From 5f92b6af2c13a1c32243946b67179a313581a288 Mon Sep 17 00:00:00 2001 From: Alex Calandra <acalandra@magento.com> Date: Wed, 27 Jun 2018 12:08:07 -0500 Subject: [PATCH 082/111] MQE-1068: Require Issue ID for Skipped Test - Added Skip text for issue Ids - Update and added test --- .../verification/Resources/SkippedTest.txt | 2 +- .../Resources/SkippedTestNoIssues.txt | 39 +++++++++++++++++++ .../Resources/SkippedTestTwoIssues.txt | 2 +- .../TestModule/Test/SkippedTest.xml | 9 +++++ .../Tests/SkippedGenerationTest.php | 11 ++++++ .../Util/TestGenerator.php | 9 ++++- 6 files changed, 69 insertions(+), 3 deletions(-) create mode 100644 dev/tests/verification/Resources/SkippedTestNoIssues.txt diff --git a/dev/tests/verification/Resources/SkippedTest.txt b/dev/tests/verification/Resources/SkippedTest.txt index 2b519cb42..b00fda574 100644 --- a/dev/tests/verification/Resources/SkippedTest.txt +++ b/dev/tests/verification/Resources/SkippedTest.txt @@ -33,6 +33,6 @@ class SkippedTestCest */ public function SkippedTest(AcceptanceTester $I, \Codeception\Scenario $scenario) { - $scenario->skip("This test is skipped"); + $scenario->skip("This test is skipped due to the following issues:\nSkippedValue"); } } diff --git a/dev/tests/verification/Resources/SkippedTestNoIssues.txt b/dev/tests/verification/Resources/SkippedTestNoIssues.txt new file mode 100644 index 000000000..8d22ca415 --- /dev/null +++ b/dev/tests/verification/Resources/SkippedTestNoIssues.txt @@ -0,0 +1,39 @@ +<?php +namespace Magento\AcceptanceTest\_default\Backend; + +use Magento\FunctionalTestingFramework\AcceptanceTester; +use Magento\FunctionalTestingFramework\DataGenerator\Handlers\DataObjectHandler; +use Magento\FunctionalTestingFramework\DataGenerator\Persist\DataPersistenceHandler; +use Magento\FunctionalTestingFramework\DataGenerator\Objects\EntityDataObject; +use Magento\FunctionalTestingFramework\DataGenerator\Handlers\CredentialStore; +use \Codeception\Util\Locator; +use Yandex\Allure\Adapter\Annotation\Features; +use Yandex\Allure\Adapter\Annotation\Stories; +use Yandex\Allure\Adapter\Annotation\Title; +use Yandex\Allure\Adapter\Annotation\Description; +use Yandex\Allure\Adapter\Annotation\Parameter; +use Yandex\Allure\Adapter\Annotation\Severity; +use Yandex\Allure\Adapter\Model\SeverityLevel; +use Yandex\Allure\Adapter\Annotation\TestCaseId; + +/** + * @Title("skippedNoIssuesTest") + * @Description("") + * @group skip + */ +class SkippedTestNoIssuesCest +{ + /** + * @Stories({"skippedNo"}) + * @Severity(level = SeverityLevel::MINOR) + * @Features({"TestModule"}) + * @Parameter(name = "AcceptanceTester", value="$I") + * @param AcceptanceTester $I + * @return void + * @throws \Exception + */ + public function SkippedTestNoIssues(AcceptanceTester $I, \Codeception\Scenario $scenario) + { + $scenario->skip("This test is skipped due to the following issues:\nNo issues have been specified."); + } +} diff --git a/dev/tests/verification/Resources/SkippedTestTwoIssues.txt b/dev/tests/verification/Resources/SkippedTestTwoIssues.txt index 48f473e7e..cd140e440 100644 --- a/dev/tests/verification/Resources/SkippedTestTwoIssues.txt +++ b/dev/tests/verification/Resources/SkippedTestTwoIssues.txt @@ -33,6 +33,6 @@ class SkippedTestTwoIssuesCest */ public function SkippedTestTwoIssues(AcceptanceTester $I, \Codeception\Scenario $scenario) { - $scenario->skip("This test is skipped"); + $scenario->skip("This test is skipped due to the following issues:\nSkippedValue\nSecondSkippedValue"); } } diff --git a/dev/tests/verification/TestModule/Test/SkippedTest.xml b/dev/tests/verification/TestModule/Test/SkippedTest.xml index 6470c967b..bceb619f8 100644 --- a/dev/tests/verification/TestModule/Test/SkippedTest.xml +++ b/dev/tests/verification/TestModule/Test/SkippedTest.xml @@ -31,4 +31,13 @@ </skip> </annotations> </test> + <test name="SkippedTestNoIssues"> + <annotations> + <stories value="skippedNo"/> + <title value="skippedNoIssuesTest"/> + <description value=""/> + <severity value="AVERAGE"/> + <group value="skip"/> + </annotations> + </test> </tests> diff --git a/dev/tests/verification/Tests/SkippedGenerationTest.php b/dev/tests/verification/Tests/SkippedGenerationTest.php index f889b8443..6a65df8e3 100644 --- a/dev/tests/verification/Tests/SkippedGenerationTest.php +++ b/dev/tests/verification/Tests/SkippedGenerationTest.php @@ -30,4 +30,15 @@ public function testMultipleSkippedIssuesGeneration() { $this->generateAndCompareTest('SkippedTestTwoIssues'); } + + /** + * Tests skipped test generation with no specified issues. Will be deprecated after MFTF 3.0.0 + * + * @throws \Exception + * @throws \Magento\FunctionalTestingFramework\Exceptions\TestReferenceException + */ + public function testSkippedNoIssueGeneration() + { + $this->generateAndCompareTest('SkippedTestNoIssues'); + } } diff --git a/src/Magento/FunctionalTestingFramework/Util/TestGenerator.php b/src/Magento/FunctionalTestingFramework/Util/TestGenerator.php index 022ccefc4..79b63a491 100644 --- a/src/Magento/FunctionalTestingFramework/Util/TestGenerator.php +++ b/src/Magento/FunctionalTestingFramework/Util/TestGenerator.php @@ -1529,7 +1529,14 @@ private function generateTestPhp($test) $testAnnotations = $this->generateAnnotationsPhp($test->getAnnotations(), true); $dependencies = 'AcceptanceTester $I'; if ($test->isSkipped()) { - $steps = "\t\t" . '$scenario->skip("This test is skipped");' . "\n"; + $skipString = "This test is skipped due to the following issues:\\n"; + $issues = $test->getAnnotations()['skip'] ?? null; + if (isset($issues)) { + $skipString .= implode("\\n", $issues); + } else { + $skipString .= "No issues have been specified."; + } + $steps = "\t\t" . '$scenario->skip("' . $skipString . '");' . "\n"; $dependencies .= ', \Codeception\Scenario $scenario'; } else { try { From cb5f51bd32078c146149c4577c12760f3ffd30d1 Mon Sep 17 00:00:00 2001 From: Alex Calandra <acalandra@magento.com> Date: Wed, 27 Jun 2018 15:38:26 -0500 Subject: [PATCH 083/111] MQE-1068: Require Issue ID for Skipped Test - Fixed Tests --- dev/tests/verification/Resources/ChildExtendedTestNoParent.txt | 2 +- dev/tests/verification/Resources/MergeSkip.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/dev/tests/verification/Resources/ChildExtendedTestNoParent.txt b/dev/tests/verification/Resources/ChildExtendedTestNoParent.txt index 130bc1599..3ab774b3d 100644 --- a/dev/tests/verification/Resources/ChildExtendedTestNoParent.txt +++ b/dev/tests/verification/Resources/ChildExtendedTestNoParent.txt @@ -34,6 +34,6 @@ class ChildExtendedTestNoParentCest */ public function ChildExtendedTestNoParent(AcceptanceTester $I, \Codeception\Scenario $scenario) { - $scenario->skip("This test is skipped"); + $scenario->skip("This test is skipped due to the following issues:\nNo issues have been specified."); } } diff --git a/dev/tests/verification/Resources/MergeSkip.txt b/dev/tests/verification/Resources/MergeSkip.txt index 180c640fe..d6ecff5db 100644 --- a/dev/tests/verification/Resources/MergeSkip.txt +++ b/dev/tests/verification/Resources/MergeSkip.txt @@ -29,6 +29,6 @@ class MergeSkipCest */ public function MergeSkip(AcceptanceTester $I, \Codeception\Scenario $scenario) { - $scenario->skip("This test is skipped"); + $scenario->skip("This test is skipped due to the following issues:\nIssue5"); } } From e7ab0b68ba6d92c0765c215789a2d0e58cc2e908 Mon Sep 17 00:00:00 2001 From: Alex Calandra <acalandra@magento.com> Date: Wed, 27 Jun 2018 16:05:43 -0500 Subject: [PATCH 084/111] MQE-1068: Require Issue ID for Skipped Test - Added reason and description to skip tag --- .../verification/TestModule/Test/SkippedTest.xml | 4 ++-- .../TestModuleMerged/Test/MergeFunctionalTest.xml | 2 +- .../Test/etc/mergedTestSchema.xsd | 11 +++++++++++ 3 files changed, 14 insertions(+), 3 deletions(-) diff --git a/dev/tests/verification/TestModule/Test/SkippedTest.xml b/dev/tests/verification/TestModule/Test/SkippedTest.xml index bceb619f8..72334a573 100644 --- a/dev/tests/verification/TestModule/Test/SkippedTest.xml +++ b/dev/tests/verification/TestModule/Test/SkippedTest.xml @@ -14,7 +14,7 @@ <title value="skippedTest"/> <description value=""/> <severity value="AVERAGE"/> - <skip> + <skip reason="CORE_ISSUE" description="Information"> <issueId value="SkippedValue"/> </skip> </annotations> @@ -25,7 +25,7 @@ <title value="skippedMultipleIssuesTest"/> <description value=""/> <severity value="AVERAGE"/> - <skip> + <skip reason="MFTF_ISSUE"> <issueId value="SkippedValue"/> <issueId value="SecondSkippedValue"/> </skip> diff --git a/dev/tests/verification/TestModuleMerged/Test/MergeFunctionalTest.xml b/dev/tests/verification/TestModuleMerged/Test/MergeFunctionalTest.xml index 912854cc4..ce8b64d63 100644 --- a/dev/tests/verification/TestModuleMerged/Test/MergeFunctionalTest.xml +++ b/dev/tests/verification/TestModuleMerged/Test/MergeFunctionalTest.xml @@ -28,7 +28,7 @@ </test> <test name="MergeSkip"> <annotations> - <skip> + <skip reason="UNKNOWN" description="Short Explanation"> <issueId value="Issue5"/> </skip> </annotations> diff --git a/src/Magento/FunctionalTestingFramework/Test/etc/mergedTestSchema.xsd b/src/Magento/FunctionalTestingFramework/Test/etc/mergedTestSchema.xsd index b65843bae..51b2eca6f 100644 --- a/src/Magento/FunctionalTestingFramework/Test/etc/mergedTestSchema.xsd +++ b/src/Magento/FunctionalTestingFramework/Test/etc/mergedTestSchema.xsd @@ -42,6 +42,15 @@ <xs:enumeration value="CRITICAL"/> </xs:restriction> </xs:simpleType> + <xs:simpleType name="skippedReasonEnum" final="restriction"> + <xs:restriction base="xs:string"> + <xs:enumeration value="CORE_ISSUE"/> + <xs:enumeration value="MFTF_ISSUE"/> + <xs:enumeration value="UNKNOWN"/> + <xs:enumeration value="OTHER"/> + <xs:enumeration value="NONE"/> + </xs:restriction> + </xs:simpleType> <xs:complexType name="annotationType"> <xs:simpleContent> <xs:extension base="xs:string"> @@ -60,6 +69,8 @@ <xs:sequence maxOccurs="unbounded"> <xs:element type="issueType" name="issueId" maxOccurs="unbounded"/> </xs:sequence> + <xs:attribute type="skippedReasonEnum" name="reason" use="required"/> + <xs:attribute type="xs:string" name="description"/> </xs:complexType> <xs:complexType name="issueType"> <xs:simpleContent> From 7003122df5d01c6557654d2422057420e216ae37 Mon Sep 17 00:00:00 2001 From: Kevin Kozan <kkozan@magento.com> Date: Thu, 28 Jun 2018 08:32:00 -0500 Subject: [PATCH 085/111] MQE-1073: Mandatory Annotations - Added requiredAnnotation check to AnnotationExtractor. --- .../Test/Util/AnnotationExtractorTest.php | 133 ++++++++++++++++++ .../Test/Handlers/TestObjectHandler.php | 11 +- .../Test/Util/AnnotationExtractor.php | 35 +++++ .../Test/Util/TestObjectExtractor.php | 10 +- 4 files changed, 181 insertions(+), 8 deletions(-) create mode 100644 dev/tests/unit/Magento/FunctionalTestFramework/Test/Util/AnnotationExtractorTest.php diff --git a/dev/tests/unit/Magento/FunctionalTestFramework/Test/Util/AnnotationExtractorTest.php b/dev/tests/unit/Magento/FunctionalTestFramework/Test/Util/AnnotationExtractorTest.php new file mode 100644 index 000000000..752cde89c --- /dev/null +++ b/dev/tests/unit/Magento/FunctionalTestFramework/Test/Util/AnnotationExtractorTest.php @@ -0,0 +1,133 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +namespace tests\unit\Magento\FunctionalTestFramework\Test\Util; + +use AspectMock\Proxy\Verifier; +use AspectMock\Test as AspectMock; +use Magento\FunctionalTestingFramework\Test\Util\AnnotationExtractor; +use Monolog\Handler\TestHandler; +use Monolog\Logger; +use PHPUnit\Framework\TestCase; +use tests\unit\Util\TestLoggingUtil; + +class AnnotationExtractorTest extends TestCase +{ + /** + * Before test functionality + * @return void + */ + public function setUp() + { + TestLoggingUtil::getInstance()->setMockLoggingUtil(); + } + + /** + * Annotation extractor takes in raw array and condenses it to expected format + * + * @throws \Exception + */ + public function testExtractAnnotations() + { + // Test Data + $testAnnotations = [ + "nodeName" => "annotations", + "features" => [ + [ + "nodeName" => "features", + "value" => "TestFeatures" + ] + ], + "stories" => [ + [ + "nodeName" => "stories", + "value" => "TestStories" + ] + ], + "description" => [ + [ + "nodeName" => "description", + "value" => "TestDescription" + ] + ], + "severity" => [ + [ + "nodeName" => "severity", + "value" => "CRITICAL" + ] + ], + "group" => [ + [ + "nodeName" => "group", + "value" => "TestGroup" + ] + ], + ]; + // Perform Test + $extractor = new AnnotationExtractor(); + $returnedAnnotations = $extractor->extractAnnotations($testAnnotations, "testFileName"); + + // Asserts + + $this->assertEquals("TestFeatures", $returnedAnnotations['features'][0]); + $this->assertEquals("TestStories", $returnedAnnotations['stories'][0]); + $this->assertEquals("TestDescription", $returnedAnnotations['description'][0]); + $this->assertEquals("CRITICAL", $returnedAnnotations['severity'][0]); + $this->assertEquals("TestGroup", $returnedAnnotations['group'][0]); + } + + /** + * Annotation extractor should throw warning when required annotations are missing + * + * @throws \Exception + */ + public function testMissingAnnotations() + { + // Test Data, missing title, description, and severity + $testAnnotations = [ + "nodeName" => "annotations", + "features" => [ + [ + "nodeName" => "features", + "value" => "TestFeatures" + ] + ], + "stories" => [ + [ + "nodeName" => "stories", + "value" => "TestStories" + ] + ], + "group" => [ + [ + "nodeName" => "group", + "value" => "TestGroup" + ] + ], + ]; + // Perform Test + $extractor = new AnnotationExtractor(); + $returnedAnnotations = $extractor->extractAnnotations($testAnnotations, "testFileName"); + + // Asserts + TestLoggingUtil::getInstance()->validateMockLogStatement( + 'warning', + 'Test testFileName is missing required annotations.', + [ + 'testName' => 'testFileName', + 'missingAnnotations' => "title, description, severity" + ] + ); + } + + /** + * After class functionality + * @return void + */ + public static function tearDownAfterClass() + { + TestLoggingUtil::getInstance()->clearMockLoggingUtil(); + } +} diff --git a/src/Magento/FunctionalTestingFramework/Test/Handlers/TestObjectHandler.php b/src/Magento/FunctionalTestingFramework/Test/Handlers/TestObjectHandler.php index e7ccc90ef..8e97b27d8 100644 --- a/src/Magento/FunctionalTestingFramework/Test/Handlers/TestObjectHandler.php +++ b/src/Magento/FunctionalTestingFramework/Test/Handlers/TestObjectHandler.php @@ -5,6 +5,7 @@ */ namespace Magento\FunctionalTestingFramework\Test\Handlers; +use Magento\FunctionalTestingFramework\Exceptions\Collector\ExceptionCollector; use Magento\FunctionalTestingFramework\Exceptions\TestReferenceException; use Magento\FunctionalTestingFramework\Exceptions\XmlException; use Magento\FunctionalTestingFramework\ObjectManager\ObjectHandlerInterface; @@ -136,13 +137,19 @@ private function initTestData() return; } + $exceptionCollector = new ExceptionCollector(); foreach ($parsedTestArray[TestObjectHandler::XML_ROOT] as $testName => $testData) { if (!is_array($testData)) { continue; } - - $this->tests[$testName] = $testObjectExtractor->extractTestData($testData); + try { + $this->tests[$testName] = $testObjectExtractor->extractTestData($testData); + } catch (XmlException $exception) { + $exceptionCollector->addError(self::class, $exception->getMessage()); + } } + $exceptionCollector->throwException(); + $testObjectExtractor->getAnnotationExtractor()->validateStoryTitleUniqueness(); } diff --git a/src/Magento/FunctionalTestingFramework/Test/Util/AnnotationExtractor.php b/src/Magento/FunctionalTestingFramework/Test/Util/AnnotationExtractor.php index c8ea53819..909b72906 100644 --- a/src/Magento/FunctionalTestingFramework/Test/Util/AnnotationExtractor.php +++ b/src/Magento/FunctionalTestingFramework/Test/Util/AnnotationExtractor.php @@ -7,6 +7,7 @@ namespace Magento\FunctionalTestingFramework\Test\Util; use Magento\FunctionalTestingFramework\Exceptions\XmlException; +use Magento\FunctionalTestingFramework\Util\Logger\LoggingUtil; /** * Class AnnotationExtractor @@ -28,6 +29,12 @@ class AnnotationExtractor extends BaseObjectExtractor "AVERAGE" => "MINOR", "MINOR" => "TRIVIAL" ]; + const REQUIRED_ANNOTATIONS = [ + "stories", + "title", + "description", + "severity" + ]; /** * AnnotationExtractor constructor. @@ -67,7 +74,11 @@ public function extractAnnotations($testAnnotations, $filename) } $annotationObjects[$annotationKey] = $annotationValues; } + + $this->validateMissingAnnotations($annotationObjects, $filename); + $this->addStoryTitleToMap($annotationObjects, $filename); + return $annotationObjects; } @@ -86,6 +97,30 @@ public function addStoryTitleToMap($annotations, $filename) } } + /** + * Validates given annotations against list of required annotations. + * @param array $annotationObjects + * @return void + */ + private function validateMissingAnnotations($annotationObjects, $filename) + { + $missingAnnotations = []; + + foreach (self::REQUIRED_ANNOTATIONS as $REQUIRED_ANNOTATION) { + if (!array_key_exists($REQUIRED_ANNOTATION, $annotationObjects)) { + $missingAnnotations[] = $REQUIRED_ANNOTATION; + } + } + + if (!empty($missingAnnotations)) { + $message = "Test {$filename} is missing required annotations."; + LoggingUtil::getInstance()->getLogger(ActionObject::class)->warning( + $message, + ["testName" => $filename, "missingAnnotations" => implode(", ", $missingAnnotations)] + ); + } + } + /** * Validates that all Story/Title combinations are unique, builds list of violators if found. * @throws XmlException diff --git a/src/Magento/FunctionalTestingFramework/Test/Util/TestObjectExtractor.php b/src/Magento/FunctionalTestingFramework/Test/Util/TestObjectExtractor.php index 0dcdedd7d..acad18572 100644 --- a/src/Magento/FunctionalTestingFramework/Test/Util/TestObjectExtractor.php +++ b/src/Magento/FunctionalTestingFramework/Test/Util/TestObjectExtractor.php @@ -110,12 +110,10 @@ public function extractTestData($testData) 'extends' ); - if (array_key_exists(self::TEST_ANNOTATIONS, $testData)) { - $testAnnotations = $this->annotationExtractor->extractAnnotations( - $testData[self::TEST_ANNOTATIONS], - $testData[self::NAME] - ); - } + $testAnnotations = $this->annotationExtractor->extractAnnotations( + $testData[self::TEST_ANNOTATIONS] ?? [], + $testData[self::NAME] + ); //Override features with module name if present, populates it otherwise $testAnnotations["features"] = [$module]; From fa2dd3fb866cecdd6caff4bdfcad418dc509c8cb Mon Sep 17 00:00:00 2001 From: Ian Meron <imeron@magento.com> Date: Wed, 20 Jun 2018 14:18:17 -0500 Subject: [PATCH 086/111] MQE-1070: Hide Sensitive Creds in Allure Report - encrypt creds for display and decrypt on execution --- dev/tests/functional/_bootstrap.php | 2 +- .../Test/Util/ActionObjectExtractorTest.php | 4 +- dev/tests/unit/Util/TestLoggingUtil.php | 9 +++ .../Resources/PersistedReplacementTest.txt | 2 +- .../Console/RunTestCommand.php | 1 + .../Handlers/CredentialStore.php | 23 ++++++- .../Module/MagentoWebDriver.php | 26 +++++--- .../Test/Objects/TestObject.php | 12 ++-- .../Test/Util/ActionMergeUtil.php | 61 ++++++++++++++++++- .../Test/Util/ActionObjectExtractor.php | 15 +++-- .../Test/etc/actionTypeTags.xsd | 1 + .../Util/Iterator/File.php | 4 ++ .../Util/TestGenerator.php | 2 +- 13 files changed, 134 insertions(+), 28 deletions(-) diff --git a/dev/tests/functional/_bootstrap.php b/dev/tests/functional/_bootstrap.php index 51dcf2063..5e6621fe0 100755 --- a/dev/tests/functional/_bootstrap.php +++ b/dev/tests/functional/_bootstrap.php @@ -4,7 +4,7 @@ * See COPYING.txt for license details. */ -define('PROJECT_ROOT', dirname(dirname(dirname(__DIR__)))); +defined('PROJECT_ROOT') || define('PROJECT_ROOT', dirname(dirname(dirname(__DIR__)))); require_once realpath(PROJECT_ROOT . '/vendor/autoload.php'); //Load constants from .env file diff --git a/dev/tests/unit/Magento/FunctionalTestFramework/Test/Util/ActionObjectExtractorTest.php b/dev/tests/unit/Magento/FunctionalTestFramework/Test/Util/ActionObjectExtractorTest.php index 183c9226d..f584adea9 100644 --- a/dev/tests/unit/Magento/FunctionalTestFramework/Test/Util/ActionObjectExtractorTest.php +++ b/dev/tests/unit/Magento/FunctionalTestFramework/Test/Util/ActionObjectExtractorTest.php @@ -48,9 +48,9 @@ public function testInvalidMergeOrderReference() try { $this->testActionObjectExtractor->extractActions($invalidArray, 'TestWithSelfReferencingStepKey'); } catch (\Exception $e) { - TestLoggingUtil::getInstance()->validateMockLogStatement( + TestLoggingUtil::getInstance()->validateMockLogStatmentRegex( 'error', - 'Line 108: Invalid ordering configuration in test', + '/Line \d*: Invalid ordering configuration in test/', [ 'test' => 'TestWithSelfReferencingStepKey', 'stepKey' => ['invalidTestAction1'] diff --git a/dev/tests/unit/Util/TestLoggingUtil.php b/dev/tests/unit/Util/TestLoggingUtil.php index 4be16280f..7c2ecc142 100644 --- a/dev/tests/unit/Util/TestLoggingUtil.php +++ b/dev/tests/unit/Util/TestLoggingUtil.php @@ -82,6 +82,15 @@ public function validateMockLogStatement($type, $message, $context) $this->assertEquals($context, $record['context']); } + public function validateMockLogStatmentRegex($type, $regex, $context) + { + $records = $this->testLogHandler->getRecords(); + $record = $records[count($records)-1]; // we assume the latest record is what requires validation + $this->assertEquals(strtoupper($type), $record['level_name']); + $this->assertRegExp($regex, $record['message']); + $this->assertEquals($context, $record['context']); + } + /** * Function which clears the test logger context from the LogginUtil class. Should be run after a test class has * executed. diff --git a/dev/tests/verification/Resources/PersistedReplacementTest.txt b/dev/tests/verification/Resources/PersistedReplacementTest.txt index f4f9b0298..78cdd5029 100644 --- a/dev/tests/verification/Resources/PersistedReplacementTest.txt +++ b/dev/tests/verification/Resources/PersistedReplacementTest.txt @@ -53,7 +53,7 @@ class PersistedReplacementTestCest $I->fillField("#selector", "StringBefore " . $createdData->getCreatedDataByName('firstname') . " StringAfter"); $I->fillField("#" . $createdData->getCreatedDataByName('firstname'), "input"); $I->fillField("#" . getenv("MAGENTO_BASE_URL") . "#" . $createdData->getCreatedDataByName('firstname'), "input"); - $I->fillField("#" . CredentialStore::getInstance()->getSecret("SECRET_PARAM") . "#" . $createdData->getCreatedDataByName('firstname'), "input"); + $I->fillSecretField("#" . CredentialStore::getInstance()->getSecret("SECRET_PARAM") . "#" . $createdData->getCreatedDataByName('firstname'), "input"); $I->dragAndDrop("#" . $createdData->getCreatedDataByName('firstname'), $createdData->getCreatedDataByName('lastname')); $I->conditionalClick($createdData->getCreatedDataByName('lastname'), "#" . $createdData->getCreatedDataByName('firstname'), true); $I->amOnUrl($createdData->getCreatedDataByName('firstname') . ".html"); diff --git a/src/Magento/FunctionalTestingFramework/Console/RunTestCommand.php b/src/Magento/FunctionalTestingFramework/Console/RunTestCommand.php index 4caf4287a..3c06a8ff4 100644 --- a/src/Magento/FunctionalTestingFramework/Console/RunTestCommand.php +++ b/src/Magento/FunctionalTestingFramework/Console/RunTestCommand.php @@ -13,6 +13,7 @@ use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Debug\Debug; use Symfony\Component\Process\Process; class RunTestCommand extends Command diff --git a/src/Magento/FunctionalTestingFramework/DataGenerator/Handlers/CredentialStore.php b/src/Magento/FunctionalTestingFramework/DataGenerator/Handlers/CredentialStore.php index edd5a7c18..241ccc0ff 100644 --- a/src/Magento/FunctionalTestingFramework/DataGenerator/Handlers/CredentialStore.php +++ b/src/Magento/FunctionalTestingFramework/DataGenerator/Handlers/CredentialStore.php @@ -14,12 +14,19 @@ class CredentialStore { /** - * Singletone instnace + * Singleton instance * * @var CredentialStore */ private static $INSTANCE = null; + /** + * Initial vector for open_ssl encryption. + * + * @var string + */ + private $iv = null; + /** * Key/Value paris of credential names and their corresponding values * @@ -47,6 +54,7 @@ public static function getInstance() private function __construct() { $this->readInCredentialsFile(); + $this->encryptionKey = openssl_random_pseudo_bytes(16); } /** @@ -103,8 +111,19 @@ private function readInCredentialsFile() list($key, $value) = explode("=", $credValue); if (!empty($value)) { - $this->credentials[$key] = $value; + $this->credentials[$key] = openssl_encrypt($value, "AES-128-ECB", 0, $this->iv); } } } + + /** + * Takes a value encrypted at runtime and descrypts using the object's initial vector. + * + * @param string $value + * @return string + */ + public function decryptSecretValue($value) + { + return openssl_decrypt($value, "AES-128-ECB", 0, $this->iv); + } } diff --git a/src/Magento/FunctionalTestingFramework/Module/MagentoWebDriver.php b/src/Magento/FunctionalTestingFramework/Module/MagentoWebDriver.php index ecfb6cadd..b70be8903 100644 --- a/src/Magento/FunctionalTestingFramework/Module/MagentoWebDriver.php +++ b/src/Magento/FunctionalTestingFramework/Module/MagentoWebDriver.php @@ -6,25 +6,18 @@ namespace Magento\FunctionalTestingFramework\Module; -use Codeception\Events; use Codeception\Module\WebDriver; use Codeception\Test\Descriptor; use Codeception\TestInterface; -use Facebook\WebDriver\WebDriverSelect; -use Facebook\WebDriver\WebDriverBy; -use Facebook\WebDriver\Exception\NoSuchElementException; use Facebook\WebDriver\Interactions\WebDriverActions; -use Codeception\Exception\ElementNotFound; use Codeception\Exception\ModuleConfigException; use Codeception\Exception\ModuleException; use Codeception\Util\Uri; -use Codeception\Util\ActionSequence; +use Magento\FunctionalTestingFramework\DataGenerator\Handlers\CredentialStore; use Magento\FunctionalTestingFramework\DataGenerator\Persist\Curl\WebapiExecutor; use Magento\FunctionalTestingFramework\Util\Protocol\CurlTransport; use Magento\FunctionalTestingFramework\Util\Protocol\CurlInterface; -use Magento\Setup\Exception; use Magento\FunctionalTestingFramework\Util\ConfigSanitizerUtil; -use Yandex\Allure\Adapter\Event\TestCaseFinishedEvent; use Yandex\Allure\Adapter\Support\AttachmentSupport; /** @@ -44,6 +37,8 @@ * password: admin_password * browser: chrome * ``` + * + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class MagentoWebDriver extends WebDriver { @@ -61,6 +56,8 @@ class MagentoWebDriver extends WebDriver '//div[@data-role="spinner"]' ]; + const STEP_OBJ_BACKTRACE_POS = 2; + /** * The module required fields, to be set in the suite .yml configuration file. * @@ -596,6 +593,19 @@ public function dragAndDrop($source, $target, $xOffset = null, $yOffset = null) } } + /** + * @param $field + * @param $value + */ + public function fillSecretField($field, $value) + { + // to protect any secrets from being printed to console the values are executed only at the webdriver level as a + // decrypted value + + $decryptedValue = CredentialStore::getInstance()->decryptSecretValue($value); + $this->fillField($field, $decryptedValue); + } + /** * Override for _failed method in Codeception method. Adds png and html attachments to allure report * following parent execution of test failure processing. diff --git a/src/Magento/FunctionalTestingFramework/Test/Objects/TestObject.php b/src/Magento/FunctionalTestingFramework/Test/Objects/TestObject.php index 5ff2c8803..75970f0dd 100644 --- a/src/Magento/FunctionalTestingFramework/Test/Objects/TestObject.php +++ b/src/Magento/FunctionalTestingFramework/Test/Objects/TestObject.php @@ -9,6 +9,8 @@ use Magento\FunctionalTestingFramework\Test\Handlers\ActionGroupObjectHandler; use Magento\FunctionalTestingFramework\Test\Util\ActionMergeUtil; use Magento\FunctionalTestingFramework\Test\Util\ActionObjectExtractor; +use Magento\FunctionalTestingFramework\Test\Util\TestHookObjectExtractor; +use Magento\FunctionalTestingFramework\Test\Util\TestObjectExtractor; /** * Class TestObject @@ -183,12 +185,10 @@ public function getEstimatedDuration() } $hookTime = 0; - if (array_key_exists('before', $this->hooks)) { - $hookTime += $this->calculateWeightedActionTimes($this->hooks['before']->getActions()); - } - - if (array_key_exists('after', $this->hooks)) { - $hookTime += $this->calculateWeightedActionTimes($this->hooks['after']->getActions()); + foreach ([TestObjectExtractor::TEST_BEFORE_HOOK, TestObjectExtractor::TEST_AFTER_HOOK] as $hookName) { + if (array_key_exists($hookName, $this->hooks)) { + $hookTime += $this->calculateWeightedActionTimes($this->hooks[$hookName]->getActions()); + } } $testTime = $this->calculateWeightedActionTimes($this->getOrderedActions()); diff --git a/src/Magento/FunctionalTestingFramework/Test/Util/ActionMergeUtil.php b/src/Magento/FunctionalTestingFramework/Test/Util/ActionMergeUtil.php index 99e575491..23329fa9c 100644 --- a/src/Magento/FunctionalTestingFramework/Test/Util/ActionMergeUtil.php +++ b/src/Magento/FunctionalTestingFramework/Test/Util/ActionMergeUtil.php @@ -83,7 +83,66 @@ public function resolveActionSteps($parsedSteps, $skipActionGroupResolution = fa return $this->orderedSteps; } - return $this->resolveActionGroups($this->orderedSteps); + $resolvedActions = $this->resolveActionGroups($this->orderedSteps); + return $this->resolveSecretFieldAccess($resolvedActions); + } + + /** + * Takes an array of actions and resolves any references to secret fields. The function then validates whether the + * refernece is valid and replaces the function name accordingly to hide arguments at runtime. + * + * @param ActionObject[] $resolvedActions + * @return ActionObject[] + * @throws TestReferenceException + */ + private function resolveSecretFieldAccess($resolvedActions) + { + $actions = []; + foreach ($resolvedActions as $resolvedAction) { + $action = $resolvedAction; + $hasSecretRef = $this->actionAttributeContainsSecretRef($resolvedAction->getCustomActionAttributes()); + + if ($resolvedAction->getType() !== 'fillField' && $hasSecretRef) { + throw new TestReferenceException("You cannot reference secret data outside of fill field actions"); + } + + if ($resolvedAction->getType() === 'fillField' && $hasSecretRef) { + $action = new ActionObject( + $action->getStepKey(), + 'fillSecretField', + $action->getCustomActionAttributes(), + $action->getLinkedAction(), + $action->getActionOrigin() + ); + } + + $actions[$action->getStepKey()] = $action; + } + + return $actions; + } + + /** + * Returns a boolean based on whether or not the action attributes contain a reference to a secret field. + * + * @param array $actionAttributes + * @return bool + */ + private function actionAttributeContainsSecretRef($actionAttributes) + { + foreach ($actionAttributes as $actionAttribute) { + if (is_array($actionAttribute)) { + return $this->actionAttributeContainsSecretRef($actionAttribute); + } + + preg_match_all("/{{_CREDS\.([\w]+)}}/", $actionAttribute, $matches); + + if (!empty($matches[0])) { + return true; + } + } + + return false; } /** diff --git a/src/Magento/FunctionalTestingFramework/Test/Util/ActionObjectExtractor.php b/src/Magento/FunctionalTestingFramework/Test/Util/ActionObjectExtractor.php index 7642aec31..9884167f6 100644 --- a/src/Magento/FunctionalTestingFramework/Test/Util/ActionObjectExtractor.php +++ b/src/Magento/FunctionalTestingFramework/Test/Util/ActionObjectExtractor.php @@ -59,6 +59,7 @@ public function extractActions($testActions, $testName = null) foreach ($testActions as $actionName => $actionData) { $stepKey = $actionData[self::TEST_STEP_MERGE_KEY]; + $actionType = $actionData[self::NODE_NAME]; if (empty($stepKey)) { throw new XmlException(sprintf(self::STEP_KEY_EMPTY_ERROR_MSG, $actionData['nodeName'])); @@ -79,10 +80,7 @@ public function extractActions($testActions, $testName = null) $actionAttributes['parameterArray'] = $actionData['array']['value']; } - if ($actionData[self::NODE_NAME] === self::ACTION_GROUP_TAG) { - $actionAttributes = $this->processActionGroupArgs($actionAttributes); - } - + $actionAttributes = $this->processActionGroupArgs($actionType, $actionAttributes); $linkedAction = $this->processLinkedActions($actionName, $actionData); $actions = $this->extractFieldActions($actionData, $actions); $actionAttributes = $this->extractFieldReferences($actionData, $actionAttributes); @@ -98,7 +96,7 @@ public function extractActions($testActions, $testName = null) $actions[$stepKey] = new ActionObject( $stepKey, - $actionData[self::NODE_NAME], + $actionType, $actionAttributes, $linkedAction['stepKey'], $linkedAction['order'] @@ -142,11 +140,16 @@ private function processLinkedActions($actionName, $actionData) * Takes the action group reference and parses out arguments as an array that can be passed to override defaults * defined in the action group xml. * + * @param string $actionType * @param array $actionAttributeData * @return array */ - private function processActionGroupArgs($actionAttributeData) + private function processActionGroupArgs($actionType, $actionAttributeData) { + if ($actionType !== self::ACTION_GROUP_TAG) { + return $actionAttributeData; + } + $actionAttributeArgData = []; foreach ($actionAttributeData as $attributeDataKey => $attributeDataValues) { if ($attributeDataKey == self::ACTION_GROUP_REF) { diff --git a/src/Magento/FunctionalTestingFramework/Test/etc/actionTypeTags.xsd b/src/Magento/FunctionalTestingFramework/Test/etc/actionTypeTags.xsd index 9aa772858..90671c340 100644 --- a/src/Magento/FunctionalTestingFramework/Test/etc/actionTypeTags.xsd +++ b/src/Magento/FunctionalTestingFramework/Test/etc/actionTypeTags.xsd @@ -37,6 +37,7 @@ <xs:element type="executeInSeleniumType" name="executeInSelenium" minOccurs="0" maxOccurs="unbounded"/> <xs:element type="executeJSType" name="executeJS" minOccurs="0" maxOccurs="unbounded"/> <xs:element type="fillFieldType" name="fillField" minOccurs="0" maxOccurs="unbounded"/> + <xs:element type="fillFieldType" name="fillSecretField" minOccurs="0" maxOccurs="unbounded"/> <xs:element type="loadSessionSnapshotType" name="loadSessionSnapshot" minOccurs="0" maxOccurs="unbounded"/> <xs:element type="makeScreenshotType" name="makeScreenshot" minOccurs="0" maxOccurs="unbounded"/> <xs:element type="maximizeWindowType" name="maximizeWindow" minOccurs="0" maxOccurs="unbounded"/> diff --git a/src/Magento/FunctionalTestingFramework/Util/Iterator/File.php b/src/Magento/FunctionalTestingFramework/Util/Iterator/File.php index 0186acd11..44a07f4fa 100644 --- a/src/Magento/FunctionalTestingFramework/Util/Iterator/File.php +++ b/src/Magento/FunctionalTestingFramework/Util/Iterator/File.php @@ -37,6 +37,10 @@ public function __construct(array $paths) */ public function getFilename() { + if ($this->current == null) { + return null; + } + return $this->data[$this->key()]; } diff --git a/src/Magento/FunctionalTestingFramework/Util/TestGenerator.php b/src/Magento/FunctionalTestingFramework/Util/TestGenerator.php index 99baeba41..45b9883ea 100644 --- a/src/Magento/FunctionalTestingFramework/Util/TestGenerator.php +++ b/src/Magento/FunctionalTestingFramework/Util/TestGenerator.php @@ -210,7 +210,7 @@ private function assembleTestPhp($testObject) $hookPhp = $this->generateHooksPhp($testObject->getHooks()); $testsPhp = $this->generateTestPhp($testObject); } catch (TestReferenceException $e) { - throw new TestReferenceException($e->getMessage() . " in Test \"" . $testObject->getName() . "\""); + throw new TestReferenceException($e->getMessage() . "\n" . $testObject->getFilename()); } $cestPhp = "<?php\n"; From 32a4c569358f6cb7d5e019379ba9068d03f47786 Mon Sep 17 00:00:00 2001 From: Ian Meron <imeron@magento.com> Date: Thu, 28 Jun 2018 09:07:47 -0500 Subject: [PATCH 087/111] MQE-1070: Hide Sensitive Creds in Allure Report - CR/QA review feedback - Fix merge conflicts --- .../Handlers/CredentialStoreTest.php | 38 +++++++++++++++++ .../Config/Reader/MftfFilesystem.php | 4 +- .../Handlers/CredentialStore.php | 42 ++++++++++++++++--- .../Module/MagentoWebDriver.php | 10 +++-- .../Test/Util/ActionMergeUtil.php | 2 +- .../Test/Util/ActionObjectExtractor.php | 2 +- .../Util/Iterator/File.php | 4 -- 7 files changed, 85 insertions(+), 17 deletions(-) create mode 100644 dev/tests/unit/Magento/FunctionalTestFramework/DataGenerator/Handlers/CredentialStoreTest.php diff --git a/dev/tests/unit/Magento/FunctionalTestFramework/DataGenerator/Handlers/CredentialStoreTest.php b/dev/tests/unit/Magento/FunctionalTestFramework/DataGenerator/Handlers/CredentialStoreTest.php new file mode 100644 index 000000000..a451f8dc9 --- /dev/null +++ b/dev/tests/unit/Magento/FunctionalTestFramework/DataGenerator/Handlers/CredentialStoreTest.php @@ -0,0 +1,38 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace tests\unit\Magento\FunctionalTestFramework\DataGenerator\Handlers; + +use Magento\FunctionalTestingFramework\DataGenerator\Handlers\CredentialStore; +use Magento\FunctionalTestingFramework\Util\MagentoTestCase; +use AspectMock\Test as AspectMock; + +class CredentialStoreTest extends MagentoTestCase +{ + + /** + * Test basic encryption/decryption functionality in CredentialStore class. + */ + public function testBasicEncryptDecrypt() + { + $testKey = 'myKey'; + $testValue = 'myValue'; + + AspectMock::double(CredentialStore::class, [ + 'readInCredentialsFile' => ["$testKey=$testValue"] + ]); + + $encryptedCred = CredentialStore::getInstance()->getSecret($testKey); + + // assert the value we've gotten is in fact not identical to our test value + $this->assertNotEquals($testValue, $encryptedCred); + + $actualValue = CredentialStore::getInstance()->decryptSecretValue($encryptedCred); + + // assert that we are able to successfully decrypt our secret value + $this->assertEquals($testValue, $actualValue); + } +} diff --git a/src/Magento/FunctionalTestingFramework/Config/Reader/MftfFilesystem.php b/src/Magento/FunctionalTestingFramework/Config/Reader/MftfFilesystem.php index 173792d7c..728c4cb3a 100644 --- a/src/Magento/FunctionalTestingFramework/Config/Reader/MftfFilesystem.php +++ b/src/Magento/FunctionalTestingFramework/Config/Reader/MftfFilesystem.php @@ -48,7 +48,9 @@ public function readFiles($fileList) } } $exceptionCollector->throwException(); - $this->validateSchema($configMerger, $fileList->getFilename()); + if ($fileList->valid()) { + $this->validateSchema($configMerger, $fileList->getFilename()); + } $output = []; if ($configMerger) { diff --git a/src/Magento/FunctionalTestingFramework/DataGenerator/Handlers/CredentialStore.php b/src/Magento/FunctionalTestingFramework/DataGenerator/Handlers/CredentialStore.php index 241ccc0ff..a28547d18 100644 --- a/src/Magento/FunctionalTestingFramework/DataGenerator/Handlers/CredentialStore.php +++ b/src/Magento/FunctionalTestingFramework/DataGenerator/Handlers/CredentialStore.php @@ -13,6 +13,8 @@ class CredentialStore { + const ENCRYPTION_ALGO = "AES-256-CBC"; + /** * Singleton instance * @@ -27,6 +29,13 @@ class CredentialStore */ private $iv = null; + /** + * Key for open_ssl encryption/decryption + * + * @var string + */ + private $encodedKey = null; + /** * Key/Value paris of credential names and their corresponding values * @@ -53,8 +62,10 @@ public static function getInstance() */ private function __construct() { - $this->readInCredentialsFile(); - $this->encryptionKey = openssl_random_pseudo_bytes(16); + $this->encodedKey = base64_encode(openssl_random_pseudo_bytes(16)); + $this->iv = substr(hash('sha256', $this->encodedKey), 0, 16); + $creds = $this->readInCredentialsFile(); + $this->credentials = $this->encryptCredFileContents($creds); } /** @@ -85,7 +96,7 @@ public function getSecret($key) /** * Private function which reads in secret key/values from .credentials file and stores in memory as key/value pair. * - * @return void + * @return array * @throws TestFrameworkException */ private function readInCredentialsFile() @@ -103,7 +114,18 @@ private function readInCredentialsFile() ); } - $credContents = file($credsFilePath, FILE_IGNORE_NEW_LINES); + return file($credsFilePath, FILE_IGNORE_NEW_LINES); + } + + /** + * Function which takes the contents of the credentials file and encrypts the entries. + * + * @param array $credContents + * @return array + */ + private function encryptCredFileContents($credContents) + { + $encryptedCreds = []; foreach ($credContents as $credValue) { if (substr($credValue, 0, 1) === '#' || empty($credValue)) { continue; @@ -111,9 +133,17 @@ private function readInCredentialsFile() list($key, $value) = explode("=", $credValue); if (!empty($value)) { - $this->credentials[$key] = openssl_encrypt($value, "AES-128-ECB", 0, $this->iv); + $encryptedCreds[$key] = openssl_encrypt( + $value, + self::ENCRYPTION_ALGO, + $this->encodedKey, + 0, + $this->iv + ); } } + + return $encryptedCreds; } /** @@ -124,6 +154,6 @@ private function readInCredentialsFile() */ public function decryptSecretValue($value) { - return openssl_decrypt($value, "AES-128-ECB", 0, $this->iv); + return openssl_decrypt($value, self::ENCRYPTION_ALGO, $this->encodedKey, 0, $this->iv); } } diff --git a/src/Magento/FunctionalTestingFramework/Module/MagentoWebDriver.php b/src/Magento/FunctionalTestingFramework/Module/MagentoWebDriver.php index b70be8903..f1aae2487 100644 --- a/src/Magento/FunctionalTestingFramework/Module/MagentoWebDriver.php +++ b/src/Magento/FunctionalTestingFramework/Module/MagentoWebDriver.php @@ -56,8 +56,6 @@ class MagentoWebDriver extends WebDriver '//div[@data-role="spinner"]' ]; - const STEP_OBJ_BACKTRACE_POS = 2; - /** * The module required fields, to be set in the suite .yml configuration file. * @@ -594,8 +592,12 @@ public function dragAndDrop($source, $target, $xOffset = null, $yOffset = null) } /** - * @param $field - * @param $value + * Function used to fill sensitive crednetials with user data, data is decrypted immediately prior to fill to avoid + * exposure in console or log. + * + * @param string $field + * @param string $value + * @return void */ public function fillSecretField($field, $value) { diff --git a/src/Magento/FunctionalTestingFramework/Test/Util/ActionMergeUtil.php b/src/Magento/FunctionalTestingFramework/Test/Util/ActionMergeUtil.php index 23329fa9c..fb562c626 100644 --- a/src/Magento/FunctionalTestingFramework/Test/Util/ActionMergeUtil.php +++ b/src/Magento/FunctionalTestingFramework/Test/Util/ActionMergeUtil.php @@ -126,7 +126,7 @@ private function resolveSecretFieldAccess($resolvedActions) * Returns a boolean based on whether or not the action attributes contain a reference to a secret field. * * @param array $actionAttributes - * @return bool + * @return boolean */ private function actionAttributeContainsSecretRef($actionAttributes) { diff --git a/src/Magento/FunctionalTestingFramework/Test/Util/ActionObjectExtractor.php b/src/Magento/FunctionalTestingFramework/Test/Util/ActionObjectExtractor.php index 9884167f6..5cd7b7ac5 100644 --- a/src/Magento/FunctionalTestingFramework/Test/Util/ActionObjectExtractor.php +++ b/src/Magento/FunctionalTestingFramework/Test/Util/ActionObjectExtractor.php @@ -141,7 +141,7 @@ private function processLinkedActions($actionName, $actionData) * defined in the action group xml. * * @param string $actionType - * @param array $actionAttributeData + * @param array $actionAttributeData * @return array */ private function processActionGroupArgs($actionType, $actionAttributeData) diff --git a/src/Magento/FunctionalTestingFramework/Util/Iterator/File.php b/src/Magento/FunctionalTestingFramework/Util/Iterator/File.php index 44a07f4fa..0186acd11 100644 --- a/src/Magento/FunctionalTestingFramework/Util/Iterator/File.php +++ b/src/Magento/FunctionalTestingFramework/Util/Iterator/File.php @@ -37,10 +37,6 @@ public function __construct(array $paths) */ public function getFilename() { - if ($this->current == null) { - return null; - } - return $this->data[$this->key()]; } From 95062cf4402981041b0b913b82bc86114c6ce88a Mon Sep 17 00:00:00 2001 From: Kevin Kozan <kkozan@magento.com> Date: Fri, 29 Jun 2018 13:25:59 -0500 Subject: [PATCH 088/111] MQE-1096: Standardize MFTF Deprecation Warnings - added MftfLogger and deprecation() function. --- dev/tests/_bootstrap.php | 2 +- .../Test/Util/AnnotationExtractorTest.php | 2 +- dev/tests/unit/Util/TestLoggingUtil.php | 3 +- .../Config/MftfApplicationConfig.php | 3 +- .../DataGenerator/Util/DataExtensionUtil.php | 5 +-- .../Test/Objects/ActionObject.php | 2 +- .../Test/Util/AnnotationExtractor.php | 2 +- .../Util/Logger/LoggingUtil.php | 4 +-- .../Util/Logger/MftfLogger.php | 31 +++++++++++++++++++ 9 files changed, 44 insertions(+), 10 deletions(-) create mode 100644 src/Magento/FunctionalTestingFramework/Util/Logger/MftfLogger.php diff --git a/dev/tests/_bootstrap.php b/dev/tests/_bootstrap.php index 0adfc2e74..4364b7f9a 100644 --- a/dev/tests/_bootstrap.php +++ b/dev/tests/_bootstrap.php @@ -30,7 +30,7 @@ // set mftf appplication context \Magento\FunctionalTestingFramework\Config\MftfApplicationConfig::create( true, - \Magento\FunctionalTestingFramework\Config\MftfApplicationConfig::GENERATION_PHASE, + \Magento\FunctionalTestingFramework\Config\MftfApplicationConfig::UNIT_TEST_PHASE, true, false ); diff --git a/dev/tests/unit/Magento/FunctionalTestFramework/Test/Util/AnnotationExtractorTest.php b/dev/tests/unit/Magento/FunctionalTestFramework/Test/Util/AnnotationExtractorTest.php index 752cde89c..fcdad2e40 100644 --- a/dev/tests/unit/Magento/FunctionalTestFramework/Test/Util/AnnotationExtractorTest.php +++ b/dev/tests/unit/Magento/FunctionalTestFramework/Test/Util/AnnotationExtractorTest.php @@ -114,7 +114,7 @@ public function testMissingAnnotations() // Asserts TestLoggingUtil::getInstance()->validateMockLogStatement( 'warning', - 'Test testFileName is missing required annotations.', + 'DEPRECATION: Test testFileName is missing required annotations.', [ 'testName' => 'testFileName', 'missingAnnotations' => "title, description, severity" diff --git a/dev/tests/unit/Util/TestLoggingUtil.php b/dev/tests/unit/Util/TestLoggingUtil.php index 7c2ecc142..655048552 100644 --- a/dev/tests/unit/Util/TestLoggingUtil.php +++ b/dev/tests/unit/Util/TestLoggingUtil.php @@ -8,6 +8,7 @@ use AspectMock\Test as AspectMock; use Magento\FunctionalTestingFramework\Util\Logger\LoggingUtil; +use Magento\FunctionalTestingFramework\Util\Logger\MftfLogger; use Monolog\Handler\TestHandler; use Monolog\Logger; use PHPUnit\Framework\Assert; @@ -54,7 +55,7 @@ public static function getInstance() public function setMockLoggingUtil() { $this->testLogHandler = new TestHandler(); - $testLogger = new Logger('testLogger'); + $testLogger = new MftfLogger('testLogger'); $testLogger->pushHandler($this->testLogHandler); $mockLoggingUtil = AspectMock::double( LoggingUtil::class, diff --git a/src/Magento/FunctionalTestingFramework/Config/MftfApplicationConfig.php b/src/Magento/FunctionalTestingFramework/Config/MftfApplicationConfig.php index 76403e1c1..eeabdf117 100644 --- a/src/Magento/FunctionalTestingFramework/Config/MftfApplicationConfig.php +++ b/src/Magento/FunctionalTestingFramework/Config/MftfApplicationConfig.php @@ -11,7 +11,8 @@ class MftfApplicationConfig { const GENERATION_PHASE = "generation"; const EXECUTION_PHASE = "execution"; - const MFTF_PHASES = [self::GENERATION_PHASE, self::EXECUTION_PHASE]; + const UNIT_TEST_PHASE = "testing"; + const MFTF_PHASES = [self::GENERATION_PHASE, self::EXECUTION_PHASE, self::UNIT_TEST_PHASE]; /** * Determines whether the user has specified a force option for generation diff --git a/src/Magento/FunctionalTestingFramework/DataGenerator/Util/DataExtensionUtil.php b/src/Magento/FunctionalTestingFramework/DataGenerator/Util/DataExtensionUtil.php index 53f0a6e6c..1123531ed 100644 --- a/src/Magento/FunctionalTestingFramework/DataGenerator/Util/DataExtensionUtil.php +++ b/src/Magento/FunctionalTestingFramework/DataGenerator/Util/DataExtensionUtil.php @@ -52,8 +52,9 @@ public function extendEntity($entityObject) PHP_EOL ); } - if (MftfApplicationConfig::getConfig()->verboseEnabled()) { - echo("Extending Data: " . $parentEntity->getName() . " => " . $entityObject->getName() . PHP_EOL); + if (MftfApplicationConfig::getConfig()->verboseEnabled() && + MftfApplicationConfig::getConfig()->getPhase() !== MftfApplicationConfig::UNIT_TEST_PHASE) { + print("Extending Data: " . $parentEntity->getName() . " => " . $entityObject->getName() . PHP_EOL); } //get parent entity type if child does not have a type diff --git a/src/Magento/FunctionalTestingFramework/Test/Objects/ActionObject.php b/src/Magento/FunctionalTestingFramework/Test/Objects/ActionObject.php index 4a5dc4696..0c73b9c86 100644 --- a/src/Magento/FunctionalTestingFramework/Test/Objects/ActionObject.php +++ b/src/Magento/FunctionalTestingFramework/Test/Objects/ActionObject.php @@ -280,7 +280,7 @@ public function trimAssertionAttributes() if (!empty($oldAttributes)) { $appConfig = MftfApplicationConfig::getConfig(); if ($appConfig->getPhase() == MftfApplicationConfig::GENERATION_PHASE && $appConfig->verboseEnabled()) { - LoggingUtil::getInstance()->getLogger(ActionObject::class)->warning( + LoggingUtil::getInstance()->getLogger(ActionObject::class)->deprecation( "use of one line Assertion actions will be deprecated in MFTF 3.0.0, please use nested syntax", ["action" => $this->type, "stepKey" => $this->stepKey] ); diff --git a/src/Magento/FunctionalTestingFramework/Test/Util/AnnotationExtractor.php b/src/Magento/FunctionalTestingFramework/Test/Util/AnnotationExtractor.php index 909b72906..fa3f675bf 100644 --- a/src/Magento/FunctionalTestingFramework/Test/Util/AnnotationExtractor.php +++ b/src/Magento/FunctionalTestingFramework/Test/Util/AnnotationExtractor.php @@ -114,7 +114,7 @@ private function validateMissingAnnotations($annotationObjects, $filename) if (!empty($missingAnnotations)) { $message = "Test {$filename} is missing required annotations."; - LoggingUtil::getInstance()->getLogger(ActionObject::class)->warning( + LoggingUtil::getInstance()->getLogger(ActionObject::class)->deprecation( $message, ["testName" => $filename, "missingAnnotations" => implode(", ", $missingAnnotations)] ); diff --git a/src/Magento/FunctionalTestingFramework/Util/Logger/LoggingUtil.php b/src/Magento/FunctionalTestingFramework/Util/Logger/LoggingUtil.php index 51a4e5d8c..536a4a698 100644 --- a/src/Magento/FunctionalTestingFramework/Util/Logger/LoggingUtil.php +++ b/src/Magento/FunctionalTestingFramework/Util/Logger/LoggingUtil.php @@ -52,7 +52,7 @@ private function __construct() * existing instance is simply returned. * * @param string $clazz - * @return Logger + * @return MftfLogger * @throws \Exception */ public function getLogger($clazz) @@ -62,7 +62,7 @@ public function getLogger($clazz) } if (!array_key_exists($clazz, $this->loggers)) { - $logger = new Logger($clazz); + $logger = new MftfLogger($clazz); $logger->pushHandler(new StreamHandler($this->getLoggingPath())); $this->loggers[$clazz] = $logger; } diff --git a/src/Magento/FunctionalTestingFramework/Util/Logger/MftfLogger.php b/src/Magento/FunctionalTestingFramework/Util/Logger/MftfLogger.php new file mode 100644 index 000000000..5844858a0 --- /dev/null +++ b/src/Magento/FunctionalTestingFramework/Util/Logger/MftfLogger.php @@ -0,0 +1,31 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\FunctionalTestingFramework\Util\Logger; + +use Magento\FunctionalTestingFramework\Config\MftfApplicationConfig; +use Monolog\Handler\StreamHandler; +use Monolog\Logger; + +class MftfLogger extends Logger +{ + /** + * Prints a deprecation warning, as well as adding a log at the WARNING level. + * + * @param string $message The log message. + * @param array $context The log context. + * @return void + */ + public function deprecation($message, array $context = []) + { + $message = "DEPRECATION: " . $message; + // Suppress print during unit testing + if (MftfApplicationConfig::getConfig()->getPhase() !== MftfApplicationConfig::UNIT_TEST_PHASE) { + print ($message . json_encode($context) . "\n"); + } + parent::warning($message, $context); + } +} From ed8018ef582a4eb56a454cf177cceba10dddce12 Mon Sep 17 00:00:00 2001 From: Alex Calandra <acalandra@magento.com> Date: Mon, 2 Jul 2018 08:56:47 -0500 Subject: [PATCH 089/111] MQE-1074: Verify MFTF Compatibility with Phpunit 7 - Specifying Logical OR for versions --- composer.json | 4 ++-- composer.lock | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/composer.json b/composer.json index b3c94d9b5..d64c0e6b6 100755 --- a/composer.json +++ b/composer.json @@ -11,7 +11,7 @@ "require": { "php": "7.0.2|7.0.4|~7.0.6|~7.1.0|~7.2.0", "allure-framework/allure-codeception": "~1.2.6", - "codeception/codeception": "~2.3.4|~2.4.2", + "codeception/codeception": "~2.3.4 || ~2.4.2", "consolidation/robo": "^1.0.0", "epfremme/swagger-php": "^2.0", "flow/jsonpath": ">0.2", @@ -23,8 +23,8 @@ }, "require-dev": { "squizlabs/php_codesniffer": "1.5.3", - "sebastian/phpcpd": "~3.0|~4.0", "brainmaestro/composer-git-hooks": "^2.3", + "sebastian/phpcpd": "~3.0 || ~4.0", "doctrine/cache": "<1.7.0", "codeception/aspect-mock": "^3.0", "goaop/framework": "2.2.0", diff --git a/composer.lock b/composer.lock index 89870ae17..20accf183 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "fbcb8c1052351276ad267a18ee187b01", + "content-hash": "9e84bce3fbba408dd92a525d4c2b2b81", "packages": [ { "name": "allure-framework/allure-codeception", From f84b7858565015701bf8999af34a6d2803f3f1a3 Mon Sep 17 00:00:00 2001 From: Alex Calandra <acalandra@magento.com> Date: Mon, 2 Jul 2018 11:48:07 -0500 Subject: [PATCH 090/111] MQE-1074: Verify MFTF Compatibility with Phpunit 7 - Updated squizlabs for composer.lock --- composer.lock | 54 ++++++++++++++------------------------------------- 1 file changed, 15 insertions(+), 39 deletions(-) diff --git a/composer.lock b/composer.lock index 20accf183..d4302b090 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "9e84bce3fbba408dd92a525d4c2b2b81", + "content-hash": "dbc74a55a9d9fb3f163805b6bec27f4e", "packages": [ { "name": "allure-framework/allure-codeception", @@ -5272,61 +5272,37 @@ }, { "name": "squizlabs/php_codesniffer", - "version": "1.5.3", + "version": "3.3.0", "source": { "type": "git", "url": "https://github.com/squizlabs/PHP_CodeSniffer.git", - "reference": "396178ada8499ec492363587f037125bf7b07fcc" + "reference": "d86873af43b4aa9d1f39a3601cc0cfcf02b25266" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/squizlabs/PHP_CodeSniffer/zipball/396178ada8499ec492363587f037125bf7b07fcc", - "reference": "396178ada8499ec492363587f037125bf7b07fcc", + "url": "https://api.github.com/repos/squizlabs/PHP_CodeSniffer/zipball/d86873af43b4aa9d1f39a3601cc0cfcf02b25266", + "reference": "d86873af43b4aa9d1f39a3601cc0cfcf02b25266", "shasum": "" }, "require": { + "ext-simplexml": "*", "ext-tokenizer": "*", - "php": ">=5.1.2" + "ext-xmlwriter": "*", + "php": ">=5.4.0" }, - "suggest": { - "phpunit/php-timer": "dev-master" + "require-dev": { + "phpunit/phpunit": "^4.0 || ^5.0 || ^6.0 || ^7.0" }, "bin": [ - "scripts/phpcs" + "bin/phpcs", + "bin/phpcbf" ], "type": "library", "extra": { "branch-alias": { - "dev-phpcs-fixer": "2.0.x-dev" + "dev-master": "3.x-dev" } }, - "autoload": { - "classmap": [ - "CodeSniffer.php", - "CodeSniffer/CLI.php", - "CodeSniffer/Exception.php", - "CodeSniffer/File.php", - "CodeSniffer/Report.php", - "CodeSniffer/Reporting.php", - "CodeSniffer/Sniff.php", - "CodeSniffer/Tokens.php", - "CodeSniffer/Reports/", - "CodeSniffer/CommentParser/", - "CodeSniffer/Tokenizers/", - "CodeSniffer/DocGenerators/", - "CodeSniffer/Standards/AbstractPatternSniff.php", - "CodeSniffer/Standards/AbstractScopeSniff.php", - "CodeSniffer/Standards/AbstractVariableSniff.php", - "CodeSniffer/Standards/IncorrectPatternException.php", - "CodeSniffer/Standards/Generic/Sniffs/", - "CodeSniffer/Standards/MySource/Sniffs/", - "CodeSniffer/Standards/PEAR/Sniffs/", - "CodeSniffer/Standards/PSR1/Sniffs/", - "CodeSniffer/Standards/PSR2/Sniffs/", - "CodeSniffer/Standards/Squiz/Sniffs/", - "CodeSniffer/Standards/Zend/Sniffs/" - ] - }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" @@ -5337,13 +5313,13 @@ "role": "lead" } ], - "description": "PHP_CodeSniffer tokenises PHP, JavaScript and CSS files and detects violations of a defined set of coding standards.", + "description": "PHP_CodeSniffer tokenizes PHP, JavaScript and CSS files and detects violations of a defined set of coding standards.", "homepage": "http://www.squizlabs.com/php-codesniffer", "keywords": [ "phpcs", "standards" ], - "time": "2014-05-01T03:07:07+00:00" + "time": "2018-06-06T23:58:19+00:00" }, { "name": "symfony/config", From dd9f789ae5919f442a826f3bba33eedc82f6dda4 Mon Sep 17 00:00:00 2001 From: Kevin Kozan <kkozan@magento.com> Date: Mon, 2 Jul 2018 13:17:18 -0500 Subject: [PATCH 091/111] MQE-1083: Acceptance tester should be able to enter future/current dates in the date field - added generateDate action, takes date and format and saves it to a variable --- .../Resources/BasicFunctionalTest.txt | 4 +++ .../TestModule/Test/BasicFunctionalTest.xml | 1 + etc/di.xml | 2 +- .../Test/Objects/ActionObject.php | 4 ++- .../Test/etc/Actions/customActions.xsd | 36 +++++++++++++++++++ .../Util/TestGenerator.php | 25 +++++++++++++ 6 files changed, 70 insertions(+), 2 deletions(-) diff --git a/dev/tests/verification/Resources/BasicFunctionalTest.txt b/dev/tests/verification/Resources/BasicFunctionalTest.txt index 41ff55235..7fed6db42 100644 --- a/dev/tests/verification/Resources/BasicFunctionalTest.txt +++ b/dev/tests/verification/Resources/BasicFunctionalTest.txt @@ -99,6 +99,10 @@ class BasicFunctionalTestCest $executeJSKey1 = $I->executeJS("someJSFunction"); $I->fillField(".functionalTestSelector", "someInput"); $I->fillField(".functionalTestSelector", "0"); + $date = new \DateTime(); + $date->setTimestamp(strtotime("Now")); + $date->setTimezone(new \DateTimeZone("America/Los_Angeles")); + $generateDateKey = $date->format("H:i:s"); $grabAttributeFromKey1 = $I->grabAttributeFrom(".functionalTestSelector", "someInput"); $grabCookieKey1 = $I->grabCookie("grabCookieInput", ['domain' => 'www.google.com']); $grabFromCurrentUrlKey1 = $I->grabFromCurrentUrl("/grabCurrentUrl"); diff --git a/dev/tests/verification/TestModule/Test/BasicFunctionalTest.xml b/dev/tests/verification/TestModule/Test/BasicFunctionalTest.xml index bc73036f3..0a2676ef6 100644 --- a/dev/tests/verification/TestModule/Test/BasicFunctionalTest.xml +++ b/dev/tests/verification/TestModule/Test/BasicFunctionalTest.xml @@ -60,6 +60,7 @@ <executeJS function="someJSFunction" stepKey="executeJSKey1"/> <fillField selector=".functionalTestSelector" userInput="someInput" stepKey="fillFieldKey1" /> <fillField selector=".functionalTestSelector" userInput="0" stepKey="fillFieldKey2" /> + <generateDate date="Now" format="H:i:s" stepKey="generateDateKey"/> <grabAttributeFrom selector=".functionalTestSelector" userInput="someInput" stepKey="grabAttributeFromKey1" /> <grabCookie userInput="grabCookieInput" parameterArray="['domain' => 'www.google.com']" stepKey="grabCookieKey1" /> <grabFromCurrentUrl regex="/grabCurrentUrl" stepKey="grabFromCurrentUrlKey1" /> diff --git a/etc/di.xml b/etc/di.xml index 4f9f851b3..eef21e7d7 100644 --- a/etc/di.xml +++ b/etc/di.xml @@ -8,7 +8,7 @@ <!-- Entity value gets replaced in Dom.php before reading $xml --> <!DOCTYPE config [ - <!ENTITY commonTestActions "acceptPopup|actionGroup|amOnPage|amOnUrl|amOnSubdomain|appendField|assertArrayIsSorted|assertArraySubset|assertElementContainsAttribute|attachFile|cancelPopup|checkOption|clearField|click|clickWithLeftButton|clickWithRightButton|closeAdminNotification|closeTab|comment|conditionalClick|createData|deleteData|updateData|getData|dontSee|dontSeeJsError|dontSeeCheckboxIsChecked|dontSeeCookie|dontSeeCurrentUrlEquals|dontSeeCurrentUrlMatches|dontSeeElement|dontSeeElementInDOM|dontSeeInCurrentUrl|dontSeeInField|dontSeeInFormFields|dontSeeInPageSource|dontSeeInSource|dontSeeInTitle|dontSeeLink|dontSeeOptionIsSelected|doubleClick|dragAndDrop|entity|executeJS|executeInSelenium|fillField|formatMoney|grabAttributeFrom|grabCookie|grabFromCurrentUrl|grabMultiple|grabPageSource|grabTextFrom|grabValueFrom|loadSessionSnapshot|loginAsAdmin|magentoCLI|makeScreenshot|maximizeWindow|moveBack|moveForward|moveMouseOver|mSetLocale|mResetLocale|openNewTab|pauseExecution|parseFloat|performOn|pressKey|reloadPage|resetCookie|submitForm|resizeWindow|saveSessionSnapshot|scrollTo|scrollToTopOfPage|searchAndMultiSelectOption|see|seeCheckboxIsChecked|seeCookie|seeCurrentUrlEquals|seeCurrentUrlMatches|seeElement|seeElementInDOM|seeInCurrentUrl|seeInField|seeInFormFields|seeInPageSource|seeInPopup|seeInSource|seeInTitle|seeLink|seeNumberOfElements|seeOptionIsSelected|selectOption|setCookie|submitForm|switchToIFrame|switchToNextTab|switchToPreviousTab|switchToWindow|typeInPopup|uncheckOption|unselectOption|wait|waitForAjaxLoad|waitForElement|waitForElementChange|waitForElementNotVisible|waitForElementVisible|waitForJS|waitForLoadingMaskToDisappear|waitForPageLoad|waitForText|assertArrayHasKey|assertArrayNotHasKey|assertArraySubset|assertContains|assertCount|assertEmpty|assertEquals|assertFalse|assertFileExists|assertFileNotExists|assertGreaterOrEquals|assertGreaterThan|assertGreaterThanOrEqual|assertInstanceOf|assertInternalType|assertIsEmpty|assertLessOrEquals|assertLessThan|assertLessThanOrEqual|assertNotContains|assertNotEmpty|assertNotEquals|assertNotInstanceOf|assertNotNull|assertNotRegExp|assertNotSame|assertNull|assertRegExp|assertSame|assertStringStartsNotWith|assertStringStartsWith|assertTrue|expectException|fail|dontSeeFullUrlEquals|dontSee|dontSeeFullUrlMatches|dontSeeInFullUrl|seeFullUrlEquals|seeFullUrlMatches|seeInFullUrl|grabFromFullUrl"> + <!ENTITY commonTestActions "acceptPopup|actionGroup|amOnPage|amOnUrl|amOnSubdomain|appendField|assertArrayIsSorted|assertArraySubset|assertElementContainsAttribute|attachFile|cancelPopup|checkOption|clearField|click|clickWithLeftButton|clickWithRightButton|closeAdminNotification|closeTab|comment|conditionalClick|createData|deleteData|updateData|getData|dontSee|dontSeeJsError|dontSeeCheckboxIsChecked|dontSeeCookie|dontSeeCurrentUrlEquals|dontSeeCurrentUrlMatches|dontSeeElement|dontSeeElementInDOM|dontSeeInCurrentUrl|dontSeeInField|dontSeeInFormFields|dontSeeInPageSource|dontSeeInSource|dontSeeInTitle|dontSeeLink|dontSeeOptionIsSelected|doubleClick|dragAndDrop|entity|executeJS|executeInSelenium|fillField|formatMoney|generateDate|grabAttributeFrom|grabCookie|grabFromCurrentUrl|grabMultiple|grabPageSource|grabTextFrom|grabValueFrom|loadSessionSnapshot|loginAsAdmin|magentoCLI|makeScreenshot|maximizeWindow|moveBack|moveForward|moveMouseOver|mSetLocale|mResetLocale|openNewTab|pauseExecution|parseFloat|performOn|pressKey|reloadPage|resetCookie|submitForm|resizeWindow|saveSessionSnapshot|scrollTo|scrollToTopOfPage|searchAndMultiSelectOption|see|seeCheckboxIsChecked|seeCookie|seeCurrentUrlEquals|seeCurrentUrlMatches|seeElement|seeElementInDOM|seeInCurrentUrl|seeInField|seeInFormFields|seeInPageSource|seeInPopup|seeInSource|seeInTitle|seeLink|seeNumberOfElements|seeOptionIsSelected|selectOption|setCookie|submitForm|switchToIFrame|switchToNextTab|switchToPreviousTab|switchToWindow|typeInPopup|uncheckOption|unselectOption|wait|waitForAjaxLoad|waitForElement|waitForElementChange|waitForElementNotVisible|waitForElementVisible|waitForJS|waitForLoadingMaskToDisappear|waitForPageLoad|waitForText|assertArrayHasKey|assertArrayNotHasKey|assertArraySubset|assertContains|assertCount|assertEmpty|assertEquals|assertFalse|assertFileExists|assertFileNotExists|assertGreaterOrEquals|assertGreaterThan|assertGreaterThanOrEqual|assertInstanceOf|assertInternalType|assertIsEmpty|assertLessOrEquals|assertLessThan|assertLessThanOrEqual|assertNotContains|assertNotEmpty|assertNotEquals|assertNotInstanceOf|assertNotNull|assertNotRegExp|assertNotSame|assertNull|assertRegExp|assertSame|assertStringStartsNotWith|assertStringStartsWith|assertTrue|expectException|fail|dontSeeFullUrlEquals|dontSee|dontSeeFullUrlMatches|dontSeeInFullUrl|seeFullUrlEquals|seeFullUrlMatches|seeInFullUrl|grabFromFullUrl"> ]> <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../src/Magento/FunctionalTestingFramework/ObjectManager/etc/config.xsd"> diff --git a/src/Magento/FunctionalTestingFramework/Test/Objects/ActionObject.php b/src/Magento/FunctionalTestingFramework/Test/Objects/ActionObject.php index 0c73b9c86..302b415d1 100644 --- a/src/Magento/FunctionalTestingFramework/Test/Objects/ActionObject.php +++ b/src/Magento/FunctionalTestingFramework/Test/Objects/ActionObject.php @@ -39,7 +39,9 @@ class ActionObject "expectedResult", "actualResult", "command", - "regex" + "regex", + "date", + "format" ]; const SELECTOR_ENABLED_ATTRIBUTES = [ 'selector', diff --git a/src/Magento/FunctionalTestingFramework/Test/etc/Actions/customActions.xsd b/src/Magento/FunctionalTestingFramework/Test/etc/Actions/customActions.xsd index 313e381d6..3a002e126 100644 --- a/src/Magento/FunctionalTestingFramework/Test/etc/Actions/customActions.xsd +++ b/src/Magento/FunctionalTestingFramework/Test/etc/Actions/customActions.xsd @@ -22,6 +22,7 @@ <xs:element type="scrollToTopOfPageType" name="scrollToTopOfPage" minOccurs="0" maxOccurs="unbounded"/> <xs:element type="clearFieldType" name="clearField" minOccurs="0" maxOccurs="unbounded"/> <xs:element type="assertArrayIsSortedType" name="assertArrayIsSorted" minOccurs="0" maxOccurs="unbounded"/> + <xs:element type="generateDateType" name="generateDate" minOccurs="0" maxOccurs="unbounded"/> </xs:choice> </xs:group> @@ -219,6 +220,41 @@ <xs:attributeGroup ref="commonActionAttributes"/> </xs:complexType> + <xs:complexType name="generateDateType"> + <xs:annotation> + <xs:documentation> + Generates a date according to input and format. + </xs:documentation> + </xs:annotation> + <xs:simpleContent> + <xs:extension base="xs:string"> + <xs:attribute name="date" use="required" type="xs:string"> + <xs:annotation> + <xs:documentation> + Date input to parse, uses same functionality as php strtotime() function. + </xs:documentation> + </xs:annotation> + </xs:attribute> + <xs:attribute name="format" use="required" type="xs:string"> + <xs:annotation> + <xs:documentation> + Format to save given date in, uses same functionality as php date() function. + </xs:documentation> + </xs:annotation> + </xs:attribute> + <xs:attribute name="timezone" use="optional" type="xs:string"> + <xs:annotation> + <xs:documentation> + Timezone to generate date in, defaults to "America/Los_Angeles". + </xs:documentation> + </xs:annotation> + </xs:attribute> + <xs:attributeGroup ref="commonActionAttributes"/> + </xs:extension> + </xs:simpleContent> + </xs:complexType> + + <xs:complexType name="arrayType"> <xs:simpleContent> <xs:extension base="xs:string"> diff --git a/src/Magento/FunctionalTestingFramework/Util/TestGenerator.php b/src/Magento/FunctionalTestingFramework/Util/TestGenerator.php index 45b9883ea..5c2b18d1f 100644 --- a/src/Magento/FunctionalTestingFramework/Util/TestGenerator.php +++ b/src/Magento/FunctionalTestingFramework/Util/TestGenerator.php @@ -491,6 +491,7 @@ public function generateStepsPhp($actionObjects, $hookObject = false, $actor = " $arguments = null; $sortOrder = null; $storeCode = null; + $format = null; $assertExpected = null; $assertActual = null; @@ -531,6 +532,17 @@ public function generateStepsPhp($actionObjects, $hookObject = false, $actor = " $input = $this->addUniquenessFunctionCall($customActionAttributes['regex']); } + if (isset($customActionAttributes['date']) && isset($customActionAttributes['format'])) { + $input = $this->addUniquenessFunctionCall($customActionAttributes['date']); + if ($input === "") { + $input = "\"Now\""; + } + $format = $this->addUniquenessFunctionCall($customActionAttributes['format']); + if ($format === "") { + $format = "\"r\""; + } + } + if (isset($customActionAttributes['expected'])) { $assertExpected = $this->resolveValueByType( $customActionAttributes['expected'], @@ -1238,6 +1250,19 @@ public function generateStepsPhp($actionObjects, $hookObject = false, $actor = " $argRef .= str_replace(ucfirst($fieldKey), "", $stepKey) . "Fields['{$fieldKey}'] = ${input};\n"; $testSteps .= $argRef; break; + case "generateDate": + $timezone = "America/Los_Angeles"; + if (isset($customActionAttributes['timezone'])) { + $timezone = $customActionAttributes['timezone']; + } + + $dateGenerateCode = "\t\t\$date = new \DateTime();\n"; + $dateGenerateCode .= "\t\t\$date->setTimestamp(strtotime({$input}));\n"; + $dateGenerateCode .= "\t\t\$date->setTimezone(new \DateTimeZone(\"{$timezone}\"));\n"; + $dateGenerateCode .= "\t\t\${$stepKey} = \$date->format({$format});\n"; + + $testSteps .= $dateGenerateCode; + break; default: $testSteps .= $this->wrapFunctionCall($actor, $actionObject, $selector, $input, $parameter); } From acb220a29508368ee75a96c9dd5d037fdafc956c Mon Sep 17 00:00:00 2001 From: Alex Calandra <acalandra@magento.com> Date: Mon, 2 Jul 2018 14:49:59 -0500 Subject: [PATCH 092/111] MQE-1074: Verify MFTF Compatibility with Phpunit 7 - Removed requires listed in MFTF --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index d35b869a3..16db10c6a 100755 --- a/composer.json +++ b/composer.json @@ -11,7 +11,7 @@ "require": { "php": "7.0.2|7.0.4|~7.0.6|~7.1.0|~7.2.0", "allure-framework/allure-codeception": "~1.2.6", - "codeception/codeception": "~2.3.4 || ~2.4.2", + "consolidation/robo": "^1.0.0", "epfremme/swagger-php": "^2.0", "flow/jsonpath": ">0.2", From 299b62e0897911f732613ec6709749528de542b8 Mon Sep 17 00:00:00 2001 From: Alex Calandra <acalandra@magento.com> Date: Mon, 2 Jul 2018 14:52:58 -0500 Subject: [PATCH 093/111] MQE-1074: Verify MFTF Compatibility with Phpunit 7 -Pushed to wrong branch --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 16db10c6a..d35b869a3 100755 --- a/composer.json +++ b/composer.json @@ -11,7 +11,7 @@ "require": { "php": "7.0.2|7.0.4|~7.0.6|~7.1.0|~7.2.0", "allure-framework/allure-codeception": "~1.2.6", - + "codeception/codeception": "~2.3.4 || ~2.4.2", "consolidation/robo": "^1.0.0", "epfremme/swagger-php": "^2.0", "flow/jsonpath": ">0.2", From 80f794ccd534f87a9e56efd6aa37c54c34152d2e Mon Sep 17 00:00:00 2001 From: Alex Calandra <acalandra@magento.com> Date: Mon, 2 Jul 2018 16:11:43 -0500 Subject: [PATCH 094/111] MQE-1074: Verify MFTF Compatibility with Phpunit 7 - Updating to correct dependency order --- composer.json | 4 +- composer.lock | 546 +++++++++++++++++++++++++------------------------- 2 files changed, 274 insertions(+), 276 deletions(-) diff --git a/composer.json b/composer.json index d35b869a3..4fbea4c38 100755 --- a/composer.json +++ b/composer.json @@ -11,7 +11,7 @@ "require": { "php": "7.0.2|7.0.4|~7.0.6|~7.1.0|~7.2.0", "allure-framework/allure-codeception": "~1.2.6", - "codeception/codeception": "~2.3.4 || ~2.4.2", + "codeception/codeception": "~2.3.4 || ~2.4.0", "consolidation/robo": "^1.0.0", "epfremme/swagger-php": "^2.0", "flow/jsonpath": ">0.2", @@ -23,8 +23,8 @@ }, "require-dev": { "squizlabs/php_codesniffer": "~3.2", - "brainmaestro/composer-git-hooks": "^2.3", "sebastian/phpcpd": "~3.0 || ~4.0", + "brainmaestro/composer-git-hooks": "^2.3", "doctrine/cache": "<1.7.0", "codeception/aspect-mock": "^3.0", "goaop/framework": "2.2.0", diff --git a/composer.lock b/composer.lock index d4302b090..2f6dfca45 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "dbc74a55a9d9fb3f163805b6bec27f4e", + "content-hash": "d44cd018c6bc05634270f2b317727ad7", "packages": [ { "name": "allure-framework/allure-codeception", @@ -111,16 +111,16 @@ }, { "name": "behat/gherkin", - "version": "v4.5.1", + "version": "v4.4.5", "source": { "type": "git", "url": "https://github.com/Behat/Gherkin.git", - "reference": "74ac03d52c5e23ad8abd5c5cce4ab0e8dc1b530a" + "reference": "5c14cff4f955b17d20d088dec1bde61c0539ec74" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Behat/Gherkin/zipball/74ac03d52c5e23ad8abd5c5cce4ab0e8dc1b530a", - "reference": "74ac03d52c5e23ad8abd5c5cce4ab0e8dc1b530a", + "url": "https://api.github.com/repos/Behat/Gherkin/zipball/5c14cff4f955b17d20d088dec1bde61c0539ec74", + "reference": "5c14cff4f955b17d20d088dec1bde61c0539ec74", "shasum": "" }, "require": { @@ -166,32 +166,32 @@ "gherkin", "parser" ], - "time": "2017-08-30T11:04:43+00:00" + "time": "2016-10-30T11:50:56+00:00" }, { "name": "codeception/codeception", - "version": "2.4.3", + "version": "2.4.1", "source": { "type": "git", "url": "https://github.com/Codeception/Codeception.git", - "reference": "13b2db0d54068afaabf3ca8ac8b6591d69018f46" + "reference": "bca3547632556875f1cdd567d6057cc14fe472b8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Codeception/Codeception/zipball/13b2db0d54068afaabf3ca8ac8b6591d69018f46", - "reference": "13b2db0d54068afaabf3ca8ac8b6591d69018f46", + "url": "https://api.github.com/repos/Codeception/Codeception/zipball/bca3547632556875f1cdd567d6057cc14fe472b8", + "reference": "bca3547632556875f1cdd567d6057cc14fe472b8", "shasum": "" }, "require": { "behat/gherkin": "^4.4.0", "codeception/phpunit-wrapper": "^6.0.9|^7.0.6", - "codeception/stub": "^2.0", + "codeception/stub": "^1.0", "ext-json": "*", "ext-mbstring": "*", "facebook/webdriver": ">=1.1.3 <2.0", "guzzlehttp/guzzle": ">=4.1.4 <7.0", "guzzlehttp/psr7": "~1.0", - "php": ">=5.6.0 <8.0", + "php": ">=5.4.0 <8.0", "symfony/browser-kit": ">=2.7 <5.0", "symfony/console": ">=2.7 <5.0", "symfony/css-selector": ">=2.7 <5.0", @@ -257,27 +257,30 @@ "functional testing", "unit testing" ], - "time": "2018-06-26T14:09:28+00:00" + "time": "2018-03-31T22:30:43+00:00" }, { "name": "codeception/phpunit-wrapper", - "version": "7.1.4", + "version": "6.0.10", "source": { "type": "git", "url": "https://github.com/Codeception/phpunit-wrapper.git", - "reference": "f18ed631f1eddbb603d72219f577d223b23a1f89" + "reference": "7057e599d97b02b4efb009681a43b327dbce138a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Codeception/phpunit-wrapper/zipball/f18ed631f1eddbb603d72219f577d223b23a1f89", - "reference": "f18ed631f1eddbb603d72219f577d223b23a1f89", + "url": "https://api.github.com/repos/Codeception/phpunit-wrapper/zipball/7057e599d97b02b4efb009681a43b327dbce138a", + "reference": "7057e599d97b02b4efb009681a43b327dbce138a", "shasum": "" }, "require": { - "phpunit/php-code-coverage": "^6.0", - "phpunit/phpunit": "^7.1", - "sebastian/comparator": "^3.0", - "sebastian/diff": "^3.0" + "phpunit/php-code-coverage": ">=2.2.4 <6.0", + "phpunit/phpunit": ">=4.8.28 <5.0.0 || >=5.6.3 <7.0", + "sebastian/comparator": ">1.1 <3.0", + "sebastian/diff": ">=1.4 <4.0" + }, + "replace": { + "codeception/phpunit-wrapper": "*" }, "require-dev": { "codeception/specify": "*", @@ -300,20 +303,20 @@ } ], "description": "PHPUnit classes used by Codeception", - "time": "2018-06-20T20:07:21+00:00" + "time": "2018-06-20T20:08:14+00:00" }, { "name": "codeception/stub", - "version": "2.0.1", + "version": "1.0.2", "source": { "type": "git", "url": "https://github.com/Codeception/Stub.git", - "reference": "b2eff325d8ff0b824ff659048be7be4e5767d7d0" + "reference": "95fb7a36b81890dd2e5163e7ab31310df6f1bb99" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Codeception/Stub/zipball/b2eff325d8ff0b824ff659048be7be4e5767d7d0", - "reference": "b2eff325d8ff0b824ff659048be7be4e5767d7d0", + "url": "https://api.github.com/repos/Codeception/Stub/zipball/95fb7a36b81890dd2e5163e7ab31310df6f1bb99", + "reference": "95fb7a36b81890dd2e5163e7ab31310df6f1bb99", "shasum": "" }, "require": { @@ -333,7 +336,7 @@ "MIT" ], "description": "Flexible Stub wrapper for PHPUnit's Mock Builder", - "time": "2018-05-18T14:33:08+00:00" + "time": "2018-02-18T13:56:56+00:00" }, { "name": "consolidation/annotated-command", @@ -849,32 +852,32 @@ }, { "name": "doctrine/instantiator", - "version": "1.1.0", + "version": "1.0.5", "source": { "type": "git", "url": "https://github.com/doctrine/instantiator.git", - "reference": "185b8868aa9bf7159f5f953ed5afb2d7fcdc3bda" + "reference": "8e884e78f9f0eb1329e445619e04456e64d8051d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/instantiator/zipball/185b8868aa9bf7159f5f953ed5afb2d7fcdc3bda", - "reference": "185b8868aa9bf7159f5f953ed5afb2d7fcdc3bda", + "url": "https://api.github.com/repos/doctrine/instantiator/zipball/8e884e78f9f0eb1329e445619e04456e64d8051d", + "reference": "8e884e78f9f0eb1329e445619e04456e64d8051d", "shasum": "" }, "require": { - "php": "^7.1" + "php": ">=5.3,<8.0-DEV" }, "require-dev": { "athletic/athletic": "~0.1.8", "ext-pdo": "*", "ext-phar": "*", - "phpunit/phpunit": "^6.2.3", - "squizlabs/php_codesniffer": "^3.0.2" + "phpunit/phpunit": "~4.0", + "squizlabs/php_codesniffer": "~2.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.2.x-dev" + "dev-master": "1.0.x-dev" } }, "autoload": { @@ -899,7 +902,7 @@ "constructor", "instantiate" ], - "time": "2017-07-22T11:58:36+00:00" + "time": "2015-06-14T21:17:01+00:00" }, { "name": "doctrine/lexer", @@ -1003,39 +1006,34 @@ }, { "name": "facebook/webdriver", - "version": "1.6.0", + "version": "1.5.0", "source": { "type": "git", "url": "https://github.com/facebook/php-webdriver.git", - "reference": "bd8c740097eb9f2fc3735250fc1912bc811a954e" + "reference": "86b5ca2f67173c9d34340845dd690149c886a605" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/facebook/php-webdriver/zipball/bd8c740097eb9f2fc3735250fc1912bc811a954e", - "reference": "bd8c740097eb9f2fc3735250fc1912bc811a954e", + "url": "https://api.github.com/repos/facebook/php-webdriver/zipball/86b5ca2f67173c9d34340845dd690149c886a605", + "reference": "86b5ca2f67173c9d34340845dd690149c886a605", "shasum": "" }, "require": { "ext-curl": "*", - "ext-json": "*", - "ext-mbstring": "*", "ext-zip": "*", "php": "^5.6 || ~7.0", "symfony/process": "^2.8 || ^3.1 || ^4.0" }, "require-dev": { "friendsofphp/php-cs-fixer": "^2.0", - "jakub-onderka/php-parallel-lint": "^0.9.2", - "php-coveralls/php-coveralls": "^2.0", + "guzzle/guzzle": "^3.4.1", + "php-coveralls/php-coveralls": "^1.0.2", "php-mock/php-mock-phpunit": "^1.1", "phpunit/phpunit": "^5.7", "sebastian/environment": "^1.3.4 || ^2.0 || ^3.0", "squizlabs/php_codesniffer": "^2.6", "symfony/var-dumper": "^3.3 || ^4.0" }, - "suggest": { - "ext-SimpleXML": "For Firefox profile creation" - }, "type": "library", "extra": { "branch-alias": { @@ -1059,7 +1057,7 @@ "selenium", "webdriver" ], - "time": "2018-05-16T17:37:13+00:00" + "time": "2017-11-15T11:08:09+00:00" }, { "name": "flow/jsonpath", @@ -1249,16 +1247,16 @@ }, { "name": "guzzlehttp/guzzle", - "version": "6.3.3", + "version": "6.3.0", "source": { "type": "git", "url": "https://github.com/guzzle/guzzle.git", - "reference": "407b0cb880ace85c9b63c5f9551db498cb2d50ba" + "reference": "f4db5a78a5ea468d4831de7f0bf9d9415e348699" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/guzzle/zipball/407b0cb880ace85c9b63c5f9551db498cb2d50ba", - "reference": "407b0cb880ace85c9b63c5f9551db498cb2d50ba", + "url": "https://api.github.com/repos/guzzle/guzzle/zipball/f4db5a78a5ea468d4831de7f0bf9d9415e348699", + "reference": "f4db5a78a5ea468d4831de7f0bf9d9415e348699", "shasum": "" }, "require": { @@ -1268,7 +1266,7 @@ }, "require-dev": { "ext-curl": "*", - "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.4 || ^7.0", + "phpunit/phpunit": "^4.0 || ^5.0", "psr/log": "^1.0" }, "suggest": { @@ -1277,7 +1275,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "6.3-dev" + "dev-master": "6.2-dev" } }, "autoload": { @@ -1310,7 +1308,7 @@ "rest", "web service" ], - "time": "2018-04-22T15:46:56+00:00" + "time": "2017-06-22T18:50:49+00:00" }, { "name": "guzzlehttp/promises", @@ -1838,28 +1836,25 @@ }, { "name": "myclabs/deep-copy", - "version": "1.8.1", + "version": "1.7.0", "source": { "type": "git", "url": "https://github.com/myclabs/DeepCopy.git", - "reference": "3e01bdad3e18354c3dce54466b7fbe33a9f9f7f8" + "reference": "3b8a3a99ba1f6a3952ac2747d989303cbd6b7a3e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/3e01bdad3e18354c3dce54466b7fbe33a9f9f7f8", - "reference": "3e01bdad3e18354c3dce54466b7fbe33a9f9f7f8", + "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/3b8a3a99ba1f6a3952ac2747d989303cbd6b7a3e", + "reference": "3b8a3a99ba1f6a3952ac2747d989303cbd6b7a3e", "shasum": "" }, "require": { - "php": "^7.1" - }, - "replace": { - "myclabs/deep-copy": "self.version" + "php": "^5.6 || ^7.0" }, "require-dev": { "doctrine/collections": "^1.0", "doctrine/common": "^2.6", - "phpunit/phpunit": "^7.1" + "phpunit/phpunit": "^4.1" }, "type": "library", "autoload": { @@ -1882,7 +1877,7 @@ "object", "object graph" ], - "time": "2018-06-11T23:09:50+00:00" + "time": "2017-10-19T19:58:43+00:00" }, { "name": "paragonie/random_compat", @@ -2286,23 +2281,23 @@ }, { "name": "phpspec/prophecy", - "version": "1.7.6", + "version": "1.7.5", "source": { "type": "git", "url": "https://github.com/phpspec/prophecy.git", - "reference": "33a7e3c4fda54e912ff6338c48823bd5c0f0b712" + "reference": "dfd6be44111a7c41c2e884a336cc4f461b3b2401" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpspec/prophecy/zipball/33a7e3c4fda54e912ff6338c48823bd5c0f0b712", - "reference": "33a7e3c4fda54e912ff6338c48823bd5c0f0b712", + "url": "https://api.github.com/repos/phpspec/prophecy/zipball/dfd6be44111a7c41c2e884a336cc4f461b3b2401", + "reference": "dfd6be44111a7c41c2e884a336cc4f461b3b2401", "shasum": "" }, "require": { "doctrine/instantiator": "^1.0.2", "php": "^5.3|^7.0", "phpdocumentor/reflection-docblock": "^2.0|^3.0.2|^4.0", - "sebastian/comparator": "^1.1|^2.0|^3.0", + "sebastian/comparator": "^1.1|^2.0", "sebastian/recursion-context": "^1.0|^2.0|^3.0" }, "require-dev": { @@ -2345,44 +2340,44 @@ "spy", "stub" ], - "time": "2018-04-18T13:57:24+00:00" + "time": "2018-02-19T10:16:54+00:00" }, { "name": "phpunit/php-code-coverage", - "version": "6.0.5", + "version": "5.3.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-code-coverage.git", - "reference": "4cab20a326d14de7575a8e235c70d879b569a57a" + "reference": "661f34d0bd3f1a7225ef491a70a020ad23a057a1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/4cab20a326d14de7575a8e235c70d879b569a57a", - "reference": "4cab20a326d14de7575a8e235c70d879b569a57a", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/661f34d0bd3f1a7225ef491a70a020ad23a057a1", + "reference": "661f34d0bd3f1a7225ef491a70a020ad23a057a1", "shasum": "" }, "require": { "ext-dom": "*", "ext-xmlwriter": "*", - "php": "^7.1", + "php": "^7.0", "phpunit/php-file-iterator": "^1.4.2", "phpunit/php-text-template": "^1.2.1", - "phpunit/php-token-stream": "^3.0", + "phpunit/php-token-stream": "^2.0.1", "sebastian/code-unit-reverse-lookup": "^1.0.1", - "sebastian/environment": "^3.1", + "sebastian/environment": "^3.0", "sebastian/version": "^2.0.1", "theseer/tokenizer": "^1.1" }, "require-dev": { - "phpunit/phpunit": "^7.0" + "phpunit/phpunit": "^6.0" }, "suggest": { - "ext-xdebug": "^2.6.0" + "ext-xdebug": "^2.5.5" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "6.0-dev" + "dev-master": "5.3.x-dev" } }, "autoload": { @@ -2408,7 +2403,7 @@ "testing", "xunit" ], - "time": "2018-05-28T11:49:20+00:00" + "time": "2017-12-06T09:29:45+00:00" }, { "name": "phpunit/php-file-iterator", @@ -2500,28 +2495,28 @@ }, { "name": "phpunit/php-timer", - "version": "2.0.0", + "version": "1.0.9", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-timer.git", - "reference": "8b8454ea6958c3dee38453d3bd571e023108c91f" + "reference": "3dcf38ca72b158baf0bc245e9184d3fdffa9c46f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/8b8454ea6958c3dee38453d3bd571e023108c91f", - "reference": "8b8454ea6958c3dee38453d3bd571e023108c91f", + "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/3dcf38ca72b158baf0bc245e9184d3fdffa9c46f", + "reference": "3dcf38ca72b158baf0bc245e9184d3fdffa9c46f", "shasum": "" }, "require": { - "php": "^7.1" + "php": "^5.3.3 || ^7.0" }, "require-dev": { - "phpunit/phpunit": "^7.0" + "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.0-dev" + "dev-master": "1.0-dev" } }, "autoload": { @@ -2536,7 +2531,7 @@ "authors": [ { "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", + "email": "sb@sebastian-bergmann.de", "role": "lead" } ], @@ -2545,33 +2540,33 @@ "keywords": [ "timer" ], - "time": "2018-02-01T13:07:23+00:00" + "time": "2017-02-26T11:10:40+00:00" }, { "name": "phpunit/php-token-stream", - "version": "3.0.0", + "version": "2.0.2", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-token-stream.git", - "reference": "21ad88bbba7c3d93530d93994e0a33cd45f02ace" + "reference": "791198a2c6254db10131eecfe8c06670700904db" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/21ad88bbba7c3d93530d93994e0a33cd45f02ace", - "reference": "21ad88bbba7c3d93530d93994e0a33cd45f02ace", + "url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/791198a2c6254db10131eecfe8c06670700904db", + "reference": "791198a2c6254db10131eecfe8c06670700904db", "shasum": "" }, "require": { "ext-tokenizer": "*", - "php": "^7.1" + "php": "^7.0" }, "require-dev": { - "phpunit/phpunit": "^7.0" + "phpunit/phpunit": "^6.2.4" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "3.0-dev" + "dev-master": "2.0-dev" } }, "autoload": { @@ -2594,20 +2589,20 @@ "keywords": [ "tokenizer" ], - "time": "2018-02-01T13:16:43+00:00" + "time": "2017-11-27T05:48:46+00:00" }, { "name": "phpunit/phpunit", - "version": "7.1.5", + "version": "6.5.8", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "ca64dba53b88aba6af32aebc6b388068db95c435" + "reference": "4f21a3c6b97c42952fd5c2837bb354ec0199b97b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/ca64dba53b88aba6af32aebc6b388068db95c435", - "reference": "ca64dba53b88aba6af32aebc6b388068db95c435", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/4f21a3c6b97c42952fd5c2837bb354ec0199b97b", + "reference": "4f21a3c6b97c42952fd5c2837bb354ec0199b97b", "shasum": "" }, "require": { @@ -2619,15 +2614,15 @@ "myclabs/deep-copy": "^1.6.1", "phar-io/manifest": "^1.0.1", "phar-io/version": "^1.0", - "php": "^7.1", + "php": "^7.0", "phpspec/prophecy": "^1.7", - "phpunit/php-code-coverage": "^6.0.1", + "phpunit/php-code-coverage": "^5.3", "phpunit/php-file-iterator": "^1.4.3", "phpunit/php-text-template": "^1.2.1", - "phpunit/php-timer": "^2.0", - "phpunit/phpunit-mock-objects": "^6.1.1", - "sebastian/comparator": "^3.0", - "sebastian/diff": "^3.0", + "phpunit/php-timer": "^1.0.9", + "phpunit/phpunit-mock-objects": "^5.0.5", + "sebastian/comparator": "^2.1", + "sebastian/diff": "^2.0", "sebastian/environment": "^3.1", "sebastian/exporter": "^3.1", "sebastian/global-state": "^2.0", @@ -2635,12 +2630,16 @@ "sebastian/resource-operations": "^1.0", "sebastian/version": "^2.0.1" }, + "conflict": { + "phpdocumentor/reflection-docblock": "3.0.2", + "phpunit/dbunit": "<3.0" + }, "require-dev": { "ext-pdo": "*" }, "suggest": { "ext-xdebug": "*", - "phpunit/php-invoker": "^2.0" + "phpunit/php-invoker": "^1.1" }, "bin": [ "phpunit" @@ -2648,7 +2647,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "7.1-dev" + "dev-master": "6.5.x-dev" } }, "autoload": { @@ -2674,30 +2673,33 @@ "testing", "xunit" ], - "time": "2018-04-29T15:09:19+00:00" + "time": "2018-04-10T11:38:34+00:00" }, { "name": "phpunit/phpunit-mock-objects", - "version": "6.1.2", + "version": "5.0.6", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit-mock-objects.git", - "reference": "f9756fd4f43f014cb2dca98deeaaa8ce5500a36e" + "reference": "33fd41a76e746b8fa96d00b49a23dadfa8334cdf" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit-mock-objects/zipball/f9756fd4f43f014cb2dca98deeaaa8ce5500a36e", - "reference": "f9756fd4f43f014cb2dca98deeaaa8ce5500a36e", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit-mock-objects/zipball/33fd41a76e746b8fa96d00b49a23dadfa8334cdf", + "reference": "33fd41a76e746b8fa96d00b49a23dadfa8334cdf", "shasum": "" }, "require": { "doctrine/instantiator": "^1.0.5", - "php": "^7.1", + "php": "^7.0", "phpunit/php-text-template": "^1.2.1", "sebastian/exporter": "^3.1" }, + "conflict": { + "phpunit/phpunit": "<6.0" + }, "require-dev": { - "phpunit/phpunit": "^7.0" + "phpunit/phpunit": "^6.5" }, "suggest": { "ext-soap": "*" @@ -2705,7 +2707,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "6.1-dev" + "dev-master": "5.0.x-dev" } }, "autoload": { @@ -2730,7 +2732,7 @@ "mock", "xunit" ], - "time": "2018-05-29T13:54:20+00:00" + "time": "2018-01-06T05:45:45+00:00" }, { "name": "psr/container", @@ -3005,30 +3007,30 @@ }, { "name": "sebastian/comparator", - "version": "3.0.1", + "version": "2.1.3", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/comparator.git", - "reference": "591a30922f54656695e59b1f39501aec513403da" + "reference": "34369daee48eafb2651bea869b4b15d75ccc35f9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/591a30922f54656695e59b1f39501aec513403da", - "reference": "591a30922f54656695e59b1f39501aec513403da", + "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/34369daee48eafb2651bea869b4b15d75ccc35f9", + "reference": "34369daee48eafb2651bea869b4b15d75ccc35f9", "shasum": "" }, "require": { - "php": "^7.1", - "sebastian/diff": "^3.0", + "php": "^7.0", + "sebastian/diff": "^2.0 || ^3.0", "sebastian/exporter": "^3.1" }, "require-dev": { - "phpunit/phpunit": "^7.1" + "phpunit/phpunit": "^6.4" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "3.0-dev" + "dev-master": "2.1.x-dev" } }, "autoload": { @@ -3065,33 +3067,32 @@ "compare", "equality" ], - "time": "2018-06-14T15:05:28+00:00" + "time": "2018-02-01T13:46:46+00:00" }, { "name": "sebastian/diff", - "version": "3.0.1", + "version": "2.0.1", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/diff.git", - "reference": "366541b989927187c4ca70490a35615d3fef2dce" + "reference": "347c1d8b49c5c3ee30c7040ea6fc446790e6bddd" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/366541b989927187c4ca70490a35615d3fef2dce", - "reference": "366541b989927187c4ca70490a35615d3fef2dce", + "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/347c1d8b49c5c3ee30c7040ea6fc446790e6bddd", + "reference": "347c1d8b49c5c3ee30c7040ea6fc446790e6bddd", "shasum": "" }, "require": { - "php": "^7.1" + "php": "^7.0" }, "require-dev": { - "phpunit/phpunit": "^7.0", - "symfony/process": "^2 || ^3.3 || ^4" + "phpunit/phpunit": "^6.2" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "3.0-dev" + "dev-master": "2.0-dev" } }, "autoload": { @@ -3116,12 +3117,9 @@ "description": "Diff implementation", "homepage": "https://github.com/sebastianbergmann/diff", "keywords": [ - "diff", - "udiff", - "unidiff", - "unified diff" + "diff" ], - "time": "2018-06-10T07:54:39+00:00" + "time": "2017-08-03T08:09:46+00:00" }, { "name": "sebastian/environment", @@ -3523,25 +3521,25 @@ }, { "name": "symfony/browser-kit", - "version": "v4.1.1", + "version": "v3.4.6", "source": { "type": "git", "url": "https://github.com/symfony/browser-kit.git", - "reference": "ff9ac5d5808a530b2e7f6abcf3a2412d4f9bcd62" + "reference": "490f27762705c8489bd042fe3e9377a191dba9b4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/browser-kit/zipball/ff9ac5d5808a530b2e7f6abcf3a2412d4f9bcd62", - "reference": "ff9ac5d5808a530b2e7f6abcf3a2412d4f9bcd62", + "url": "https://api.github.com/repos/symfony/browser-kit/zipball/490f27762705c8489bd042fe3e9377a191dba9b4", + "reference": "490f27762705c8489bd042fe3e9377a191dba9b4", "shasum": "" }, "require": { - "php": "^7.1.3", - "symfony/dom-crawler": "~3.4|~4.0" + "php": "^5.5.9|>=7.0.8", + "symfony/dom-crawler": "~2.8|~3.0|~4.0" }, "require-dev": { - "symfony/css-selector": "~3.4|~4.0", - "symfony/process": "~3.4|~4.0" + "symfony/css-selector": "~2.8|~3.0|~4.0", + "symfony/process": "~2.8|~3.0|~4.0" }, "suggest": { "symfony/process": "" @@ -3549,7 +3547,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "4.1-dev" + "dev-master": "3.4-dev" } }, "autoload": { @@ -3576,24 +3574,25 @@ ], "description": "Symfony BrowserKit Component", "homepage": "https://symfony.com", - "time": "2018-06-04T17:31:56+00:00" + "time": "2018-01-03T07:37:34+00:00" }, { "name": "symfony/console", - "version": "v4.1.1", + "version": "v3.4.6", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "70591cda56b4b47c55776ac78e157c4bb6c8b43f" + "reference": "067339e9b8ec30d5f19f5950208893ff026b94f7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/70591cda56b4b47c55776ac78e157c4bb6c8b43f", - "reference": "70591cda56b4b47c55776ac78e157c4bb6c8b43f", + "url": "https://api.github.com/repos/symfony/console/zipball/067339e9b8ec30d5f19f5950208893ff026b94f7", + "reference": "067339e9b8ec30d5f19f5950208893ff026b94f7", "shasum": "" }, "require": { - "php": "^7.1.3", + "php": "^5.5.9|>=7.0.8", + "symfony/debug": "~2.8|~3.0|~4.0", "symfony/polyfill-mbstring": "~1.0" }, "conflict": { @@ -3602,14 +3601,14 @@ }, "require-dev": { "psr/log": "~1.0", - "symfony/config": "~3.4|~4.0", + "symfony/config": "~3.3|~4.0", "symfony/dependency-injection": "~3.4|~4.0", - "symfony/event-dispatcher": "~3.4|~4.0", + "symfony/event-dispatcher": "~2.8|~3.0|~4.0", "symfony/lock": "~3.4|~4.0", - "symfony/process": "~3.4|~4.0" + "symfony/process": "~3.3|~4.0" }, "suggest": { - "psr/log-implementation": "For using the console logger", + "psr/log": "For using the console logger", "symfony/event-dispatcher": "", "symfony/lock": "", "symfony/process": "" @@ -3617,7 +3616,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "4.1-dev" + "dev-master": "3.4-dev" } }, "autoload": { @@ -3644,29 +3643,29 @@ ], "description": "Symfony Console Component", "homepage": "https://symfony.com", - "time": "2018-05-31T10:17:53+00:00" + "time": "2018-02-26T15:46:28+00:00" }, { "name": "symfony/css-selector", - "version": "v4.1.1", + "version": "v3.4.6", "source": { "type": "git", "url": "https://github.com/symfony/css-selector.git", - "reference": "03ac71606ecb0b0ce792faa17d74cc32c2949ef4" + "reference": "544655f1fc078a9cd839fdda2b7b1e64627c826a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/css-selector/zipball/03ac71606ecb0b0ce792faa17d74cc32c2949ef4", - "reference": "03ac71606ecb0b0ce792faa17d74cc32c2949ef4", + "url": "https://api.github.com/repos/symfony/css-selector/zipball/544655f1fc078a9cd839fdda2b7b1e64627c826a", + "reference": "544655f1fc078a9cd839fdda2b7b1e64627c826a", "shasum": "" }, "require": { - "php": "^7.1.3" + "php": "^5.5.9|>=7.0.8" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "4.1-dev" + "dev-master": "3.4-dev" } }, "autoload": { @@ -3697,29 +3696,84 @@ ], "description": "Symfony CssSelector Component", "homepage": "https://symfony.com", - "time": "2018-05-30T07:26:09+00:00" + "time": "2018-02-03T14:55:07+00:00" + }, + { + "name": "symfony/debug", + "version": "v3.4.6", + "source": { + "type": "git", + "url": "https://github.com/symfony/debug.git", + "reference": "9b1071f86e79e1999b3d3675d2e0e7684268b9bc" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/debug/zipball/9b1071f86e79e1999b3d3675d2e0e7684268b9bc", + "reference": "9b1071f86e79e1999b3d3675d2e0e7684268b9bc", + "shasum": "" + }, + "require": { + "php": "^5.5.9|>=7.0.8", + "psr/log": "~1.0" + }, + "conflict": { + "symfony/http-kernel": ">=2.3,<2.3.24|~2.4.0|>=2.5,<2.5.9|>=2.6,<2.6.2" + }, + "require-dev": { + "symfony/http-kernel": "~2.8|~3.0|~4.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.4-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\Debug\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony Debug Component", + "homepage": "https://symfony.com", + "time": "2018-02-28T21:49:22+00:00" }, { "name": "symfony/dom-crawler", - "version": "v4.1.1", + "version": "v3.4.6", "source": { "type": "git", "url": "https://github.com/symfony/dom-crawler.git", - "reference": "3350cacf151b48d903114ab8f7a4ccb23e07e10a" + "reference": "2bb5d3101cc01f4fe580e536daf4f1959bc2d24d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/dom-crawler/zipball/3350cacf151b48d903114ab8f7a4ccb23e07e10a", - "reference": "3350cacf151b48d903114ab8f7a4ccb23e07e10a", + "url": "https://api.github.com/repos/symfony/dom-crawler/zipball/2bb5d3101cc01f4fe580e536daf4f1959bc2d24d", + "reference": "2bb5d3101cc01f4fe580e536daf4f1959bc2d24d", "shasum": "" }, "require": { - "php": "^7.1.3", - "symfony/polyfill-ctype": "~1.8", + "php": "^5.5.9|>=7.0.8", "symfony/polyfill-mbstring": "~1.0" }, "require-dev": { - "symfony/css-selector": "~3.4|~4.0" + "symfony/css-selector": "~2.8|~3.0|~4.0" }, "suggest": { "symfony/css-selector": "" @@ -3727,7 +3781,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "4.1-dev" + "dev-master": "3.4-dev" } }, "autoload": { @@ -3754,34 +3808,34 @@ ], "description": "Symfony DomCrawler Component", "homepage": "https://symfony.com", - "time": "2018-05-01T23:02:13+00:00" + "time": "2018-02-22T10:48:49+00:00" }, { "name": "symfony/event-dispatcher", - "version": "v4.1.1", + "version": "v3.4.6", "source": { "type": "git", "url": "https://github.com/symfony/event-dispatcher.git", - "reference": "2391ed210a239868e7256eb6921b1bd83f3087b5" + "reference": "58990682ac3fdc1f563b7e705452921372aad11d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/2391ed210a239868e7256eb6921b1bd83f3087b5", - "reference": "2391ed210a239868e7256eb6921b1bd83f3087b5", + "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/58990682ac3fdc1f563b7e705452921372aad11d", + "reference": "58990682ac3fdc1f563b7e705452921372aad11d", "shasum": "" }, "require": { - "php": "^7.1.3" + "php": "^5.5.9|>=7.0.8" }, "conflict": { - "symfony/dependency-injection": "<3.4" + "symfony/dependency-injection": "<3.3" }, "require-dev": { "psr/log": "~1.0", - "symfony/config": "~3.4|~4.0", - "symfony/dependency-injection": "~3.4|~4.0", - "symfony/expression-language": "~3.4|~4.0", - "symfony/stopwatch": "~3.4|~4.0" + "symfony/config": "~2.8|~3.0|~4.0", + "symfony/dependency-injection": "~3.3|~4.0", + "symfony/expression-language": "~2.8|~3.0|~4.0", + "symfony/stopwatch": "~2.8|~3.0|~4.0" }, "suggest": { "symfony/dependency-injection": "", @@ -3790,7 +3844,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "4.1-dev" + "dev-master": "3.4-dev" } }, "autoload": { @@ -3817,7 +3871,7 @@ ], "description": "Symfony EventDispatcher Component", "homepage": "https://symfony.com", - "time": "2018-04-06T07:35:57+00:00" + "time": "2018-02-14T10:03:57+00:00" }, { "name": "symfony/filesystem", @@ -3870,25 +3924,25 @@ }, { "name": "symfony/finder", - "version": "v4.1.1", + "version": "v3.4.6", "source": { "type": "git", "url": "https://github.com/symfony/finder.git", - "reference": "84714b8417d19e4ba02ea78a41a975b3efaafddb" + "reference": "a479817ce0a9e4adfd7d39c6407c95d97c254625" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/finder/zipball/84714b8417d19e4ba02ea78a41a975b3efaafddb", - "reference": "84714b8417d19e4ba02ea78a41a975b3efaafddb", + "url": "https://api.github.com/repos/symfony/finder/zipball/a479817ce0a9e4adfd7d39c6407c95d97c254625", + "reference": "a479817ce0a9e4adfd7d39c6407c95d97c254625", "shasum": "" }, "require": { - "php": "^7.1.3" + "php": "^5.5.9|>=7.0.8" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "4.1-dev" + "dev-master": "3.4-dev" } }, "autoload": { @@ -3915,7 +3969,7 @@ ], "description": "Symfony Finder Component", "homepage": "https://symfony.com", - "time": "2018-06-19T21:38:16+00:00" + "time": "2018-03-05T18:28:11+00:00" }, { "name": "symfony/http-foundation", @@ -3971,73 +4025,18 @@ "homepage": "https://symfony.com", "time": "2018-02-22T10:48:49+00:00" }, - { - "name": "symfony/polyfill-ctype", - "version": "v1.8.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/polyfill-ctype.git", - "reference": "7cc359f1b7b80fc25ed7796be7d96adc9b354bae" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/7cc359f1b7b80fc25ed7796be7d96adc9b354bae", - "reference": "7cc359f1b7b80fc25ed7796be7d96adc9b354bae", - "shasum": "" - }, - "require": { - "php": ">=5.3.3" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.8-dev" - } - }, - "autoload": { - "psr-4": { - "Symfony\\Polyfill\\Ctype\\": "" - }, - "files": [ - "bootstrap.php" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - }, - { - "name": "Gert de Pagter", - "email": "BackEndTea@gmail.com" - } - ], - "description": "Symfony polyfill for ctype functions", - "homepage": "https://symfony.com", - "keywords": [ - "compatibility", - "ctype", - "polyfill", - "portable" - ], - "time": "2018-04-30T19:57:29+00:00" - }, { "name": "symfony/polyfill-mbstring", - "version": "v1.8.0", + "version": "v1.7.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-mbstring.git", - "reference": "3296adf6a6454a050679cde90f95350ad604b171" + "reference": "78be803ce01e55d3491c1397cf1c64beb9c1b63b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/3296adf6a6454a050679cde90f95350ad604b171", - "reference": "3296adf6a6454a050679cde90f95350ad604b171", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/78be803ce01e55d3491c1397cf1c64beb9c1b63b", + "reference": "78be803ce01e55d3491c1397cf1c64beb9c1b63b", "shasum": "" }, "require": { @@ -4049,7 +4048,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "1.8-dev" + "dev-master": "1.7-dev" } }, "autoload": { @@ -4083,7 +4082,7 @@ "portable", "shim" ], - "time": "2018-04-26T10:06:28+00:00" + "time": "2018-01-30T19:27:44+00:00" }, { "name": "symfony/polyfill-php70", @@ -4195,21 +4194,20 @@ }, { "name": "symfony/yaml", - "version": "v3.4.12", + "version": "v3.4.6", "source": { "type": "git", "url": "https://github.com/symfony/yaml.git", - "reference": "c5010cc1692ce1fa328b1fb666961eb3d4a85bb0" + "reference": "6af42631dcf89e9c616242c900d6c52bd53bd1bb" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/yaml/zipball/c5010cc1692ce1fa328b1fb666961eb3d4a85bb0", - "reference": "c5010cc1692ce1fa328b1fb666961eb3d4a85bb0", + "url": "https://api.github.com/repos/symfony/yaml/zipball/6af42631dcf89e9c616242c900d6c52bd53bd1bb", + "reference": "6af42631dcf89e9c616242c900d6c52bd53bd1bb", "shasum": "" }, "require": { - "php": "^5.5.9|>=7.0.8", - "symfony/polyfill-ctype": "~1.8" + "php": "^5.5.9|>=7.0.8" }, "conflict": { "symfony/console": "<3.4" @@ -4250,7 +4248,7 @@ ], "description": "Symfony Yaml Component", "homepage": "https://symfony.com", - "time": "2018-05-03T23:18:14+00:00" + "time": "2018-02-16T09:50:28+00:00" }, { "name": "theseer/tokenizer", @@ -5222,21 +5220,21 @@ }, { "name": "sebastian/phpcpd", - "version": "4.0.0", + "version": "3.0.1", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpcpd.git", - "reference": "bb7953b81fb28e55964d76d5fe2dbe725d43fab3" + "reference": "dfed51c1288790fc957c9433e2f49ab152e8a564" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpcpd/zipball/bb7953b81fb28e55964d76d5fe2dbe725d43fab3", - "reference": "bb7953b81fb28e55964d76d5fe2dbe725d43fab3", + "url": "https://api.github.com/repos/sebastianbergmann/phpcpd/zipball/dfed51c1288790fc957c9433e2f49ab152e8a564", + "reference": "dfed51c1288790fc957c9433e2f49ab152e8a564", "shasum": "" }, "require": { - "php": "^7.1", - "phpunit/php-timer": "^2.0", + "php": "^5.6|^7.0", + "phpunit/php-timer": "^1.0.6", "sebastian/finder-facade": "^1.1", "sebastian/version": "^1.0|^2.0", "symfony/console": "^2.7|^3.0|^4.0" @@ -5247,7 +5245,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "4.0-dev" + "dev-master": "3.0-dev" } }, "autoload": { @@ -5268,7 +5266,7 @@ ], "description": "Copy/Paste Detector (CPD) for PHP code.", "homepage": "https://github.com/sebastianbergmann/phpcpd", - "time": "2018-02-02T09:59:58+00:00" + "time": "2017-11-16T08:49:28+00:00" }, { "name": "squizlabs/php_codesniffer", From 5c2d01abe9b86cc73048fcd96c7192b1d8505bb6 Mon Sep 17 00:00:00 2001 From: pdohogne-magento <pdohogne@magento.com> Date: Tue, 3 Jul 2018 11:09:08 -0500 Subject: [PATCH 095/111] MAGETWO-46837: Implementing extension to wait for readiness metrics to pass before executing test steps --- etc/config/codeception.dist.yml | 11 + .../Extension/PageReadinessExtension.php | 227 ++++++++++++ .../ReadinessMetrics/AbstractMetricCheck.php | 330 ++++++++++++++++++ .../ReadinessMetrics/DocumentReadyState.php | 45 +++ .../ReadinessMetrics/JQueryAjaxRequests.php | 57 +++ .../ReadinessMetrics/MagentoLoadingMasks.php | 56 +++ .../PrototypeAjaxRequests.php | 57 +++ .../ReadinessMetrics/RequireJsDefinitions.php | 62 ++++ 8 files changed, 845 insertions(+) create mode 100644 src/Magento/FunctionalTestingFramework/Extension/PageReadinessExtension.php create mode 100644 src/Magento/FunctionalTestingFramework/Extension/ReadinessMetrics/AbstractMetricCheck.php create mode 100644 src/Magento/FunctionalTestingFramework/Extension/ReadinessMetrics/DocumentReadyState.php create mode 100644 src/Magento/FunctionalTestingFramework/Extension/ReadinessMetrics/JQueryAjaxRequests.php create mode 100644 src/Magento/FunctionalTestingFramework/Extension/ReadinessMetrics/MagentoLoadingMasks.php create mode 100644 src/Magento/FunctionalTestingFramework/Extension/ReadinessMetrics/PrototypeAjaxRequests.php create mode 100644 src/Magento/FunctionalTestingFramework/Extension/ReadinessMetrics/RequireJsDefinitions.php diff --git a/etc/config/codeception.dist.yml b/etc/config/codeception.dist.yml index 273ede0b0..57582f1bd 100755 --- a/etc/config/codeception.dist.yml +++ b/etc/config/codeception.dist.yml @@ -15,6 +15,7 @@ extensions: - Codeception\Extension\RunFailed - Magento\FunctionalTestingFramework\Extension\TestContextExtension - Magento\FunctionalTestingFramework\Allure\Adapter\MagentoAllureAdapter + - Magento\FunctionalTestingFramework\Extension\PageReadinessExtension config: Yandex\Allure\Adapter\AllureAdapter: deletePreviousResults: true @@ -23,5 +24,15 @@ extensions: - env - zephyrId - useCaseId + Magento\FunctionalTestingFramework\Extension\PageReadinessExtension: + driver: \Magento\FunctionalTestingFramework\Module\MagentoWebDriver + timeout: 30 + resetFailureThreshold: 3 + readinessMetrics: + - \Magento\FunctionalTestingFramework\Extension\ReadinessMetrics\DocumentReadyState + - \Magento\FunctionalTestingFramework\Extension\ReadinessMetrics\JQueryAjaxRequests + - \Magento\FunctionalTestingFramework\Extension\ReadinessMetrics\PrototypeAjaxRequests + - \Magento\FunctionalTestingFramework\Extension\ReadinessMetrics\RequireJsDefinitions + - \Magento\FunctionalTestingFramework\Extension\ReadinessMetrics\MagentoLoadingMasks params: - .env \ No newline at end of file diff --git a/src/Magento/FunctionalTestingFramework/Extension/PageReadinessExtension.php b/src/Magento/FunctionalTestingFramework/Extension/PageReadinessExtension.php new file mode 100644 index 000000000..f288c6f4e --- /dev/null +++ b/src/Magento/FunctionalTestingFramework/Extension/PageReadinessExtension.php @@ -0,0 +1,227 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\FunctionalTestingFramework\Extension; + +use Codeception\Event\StepEvent; +use Codeception\Event\TestEvent; +use Codeception\Events; +use Codeception\Exception\ModuleRequireException; +use Codeception\Extension; +use Codeception\Module\WebDriver; +use Codeception\TestInterface; +use Facebook\WebDriver\Exception\UnexpectedAlertOpenException; +use Magento\FunctionalTestingFramework\Extension\ReadinessMetrics\AbstractMetricCheck; +use Facebook\WebDriver\Exception\TimeOutException; +use Magento\FunctionalTestingFramework\Util\Logger\LoggingUtil; +use Magento\FunctionalTestingFramework\Config\MftfApplicationConfig; +use Monolog\Logger; + +/** + * Class PageReadinessExtension + */ +class PageReadinessExtension extends Extension +{ + /** + * Codeception Events Mapping to methods + * + * @var array + */ + public static $events = [ + Events::TEST_BEFORE => 'beforeTest', + Events::STEP_BEFORE => 'beforeStep', + Events::STEP_AFTER => 'afterStep' + ]; + + /** + * @var Logger + */ + private $logger; + + /** + * Logger verbosity + * + * @var bool + */ + private $verbose; + + /** + * Array of readiness metrics, initialized during beforeTest event + * + * @var AbstractMetricCheck[] + */ + private $readinessMetrics; + + /** + * Active test object + * + * @var TestInterface + */ + private $test; + + /** + * Initialize local vars + * + * @return void + * @throws \Exception + */ + public function _initialize() + { + $this->logger = LoggingUtil::getInstance()->getLogger(get_class($this)); + $this->verbose = MftfApplicationConfig::getConfig()->verboseEnabled(); + } + + /** + * WebDriver instance to use to execute readiness metric checks + * + * @return WebDriver + * @throws ModuleRequireException + */ + public function getDriver() + { + return $this->getModule($this->config['driver']); + } + + /** + * Initialize the readiness metrics for the test + * + * @param \Codeception\Event\TestEvent $e + * @return void + */ + public function beforeTest(TestEvent $e) { + $this->test = $e->getTest(); + + if (isset($this->config['resetFailureThreshold'])) { + $failThreshold = intval($this->config['resetFailureThreshold']); + } + else { + $failThreshold = 3; + } + + $metrics = []; + foreach ($this->config['readinessMetrics'] as $metricClass) { + $metrics[] = new $metricClass($this, $this->test, $failThreshold); + } + + $this->readinessMetrics = $metrics; + } + + /** + * Waits for busy page flags to disappear before executing a step + * + * @param StepEvent $e + * @return void + * @throws \Exception + */ + public function beforeStep(StepEvent $e) { + $step = $e->getStep(); + if ($step->getAction() == 'saveScreenshot') { + return; + } + // $step->getArguments()['skipReadiness'] + + try { + $this->test->getMetadata()->setCurrent(['uri', $this->getDriver()->_getCurrentUri()]); + } + catch (\Exception $exception) { + // $this->debugLog('Could not retrieve current URI', ['action' => $e->getStep()->getAction()]); + } + + + if (isset($this->config['timeout'])) { + $timeout = intval($this->config['timeout']); + } + else { + $timeout = $this->getDriver()->_getConfig()['pageload_timeout']; + } + + $metrics = $this->readinessMetrics; + + try { + $this->getDriver()->webDriver->wait($timeout)->until( + function () use ($metrics) { + $passing = true; + + /** @var AbstractMetricCheck $metric */ + foreach ($metrics as $metric) { + try { + if (!$metric->runCheck()) { + $passing = false; + } + } + catch (UnexpectedAlertOpenException $exception) {} + } + return $passing; + } + ); + } + catch (TimeoutException $exception) {} + + /** @var AbstractMetricCheck $metric */ + foreach ($metrics as $metric) { + $metric->finalize($step); + } + } + + /** + * Checks to see if the step changed the uri and resets failure tracking if so + * + * @param StepEvent $e + * @return void + */ + public function afterStep(StepEvent $e) { + $step = $e->getStep(); + if ($step->getAction() == 'saveScreenshot') { + return; + } + + try { + $currentUri = $this->getDriver()->_getCurrentUri(); + } + catch (\Exception $e) { + // $this->debugLog('Could not retrieve current URI', ['action' => $step()->getAction()]); + return; + } + + $previousUri = $this->test->getMetadata()->getCurrent('uri'); + + if ($previousUri !== $currentUri) { + $this->logDebug('Page URI changed; resetting readiness metric failure tracking', + [ + 'action' => $step->getAction(), + 'newUri' => $currentUri + ] + ); + + /** @var AbstractMetricCheck $metric */ + foreach ($this->readinessMetrics as $metric) { + $metric->setTracker(); + } + } + } + + /** + * If verbose, log the given message to logger->debug including test context information + * + * @param string $message + * @param array $context + */ + private function logDebug($message, $context = []) { + if ($this->verbose) { + $testMeta = $this->test->getMetadata(); + $logContext = [ + 'test' => $testMeta->getName(), + 'uri' => $testMeta->getCurrent('uri') + ]; + foreach ($this->readinessMetrics as $metric) { + $logContext[$metric->getName()] = $metric->getStoredValue(); + $logContext[$metric->getName() . '.failCount'] = $metric->getFailureCount(); + } + $context = array_merge($logContext, $context); + $this->logger->info($message, $context); + } + } +} diff --git a/src/Magento/FunctionalTestingFramework/Extension/ReadinessMetrics/AbstractMetricCheck.php b/src/Magento/FunctionalTestingFramework/Extension/ReadinessMetrics/AbstractMetricCheck.php new file mode 100644 index 000000000..78ba40c4d --- /dev/null +++ b/src/Magento/FunctionalTestingFramework/Extension/ReadinessMetrics/AbstractMetricCheck.php @@ -0,0 +1,330 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\FunctionalTestingFramework\Extension\ReadinessMetrics; + +use Codeception\Exception\ModuleRequireException; +use Codeception\Module\WebDriver; +use Codeception\Step; +use Codeception\TestInterface; +use Facebook\WebDriver\Exception\UnexpectedAlertOpenException; +use Magento\FunctionalTestingFramework\Extension\PageReadinessExtension; +use Magento\FunctionalTestingFramework\Util\Logger\LoggingUtil; +use Magento\FunctionalTestingFramework\Config\MftfApplicationConfig; +use Monolog\Logger; + +/** + * Class AbstractMetricCheck + */ +abstract class AbstractMetricCheck +{ + /** + * Extension being used to verify this metric passes before test metrics + * + * @var PageReadinessExtension + */ + protected $extension; + + /** + * The active test object + * + * @var TestInterface + */ + protected $test; + + /** + * Current state of the value the metric tracks + * + * @var mixed; + */ + protected $currentValue; + + /** + * Number of sequential identical failures before force-resetting the metric + * + * @var integer + */ + protected $resetFailureThreshold; + + /** + * @var Logger + */ + protected $logger; + + /** + * @var bool + */ + protected $verbose; + + /** + * Constructor, called from the beforeTest event + * + * @param PageReadinessExtension $extension + * @param TestInterface $test + * @param integer $resetFailureThreshold + * @throws \Exception + */ + public function __construct($extension, $test, $resetFailureThreshold){ + $this->extension = $extension; + $this->test = $test; + $this->logger = LoggingUtil::getInstance()->getLogger(get_class($this)); + $this->verbose = MftfApplicationConfig::getConfig()->verboseEnabled(); + + // If the clearFailureOnPage() method is overridden, use the configured failure threshold + // If not, the default clearFailureOnPage() method does nothing so don't worry about resetting failures + $reflector = new \ReflectionMethod($this, 'clearFailureOnPage'); + if ($reflector->getDeclaringClass()->getName() === get_class($this)) { + $this->resetFailureThreshold = $resetFailureThreshold; + } + else { + $this->resetFailureThreshold = -1; + } + + $this->setTracker(); + } + + /** + * Does the given value pass the readiness metric + * + * @param mixed $value + * @return bool + */ + protected abstract function doesMetricPass($value); + + /** + * Retrieve the active value for the metric to check from the page + * + * @return mixed + * @throws UnexpectedAlertOpenException + */ + protected abstract function fetchValueFromPage(); + + /** + * Override this method to reset the actual state of the page to make the metric pass + * This method is called when too many identical failures were encountered in a row + * + * @return void + */ + protected function clearFailureOnPage() { + return; + } + + /** + * Get the base class name of the metric implementation + * + * @return string + */ + public function getName() { + $clazz = get_class($this); + $namespaceBreak = strrpos($clazz, '\\'); + if ($namespaceBreak !== false) { + $clazz = substr($clazz, $namespaceBreak + 1); + } + return $clazz; + } + + /** + * Fetches a new value for the metric and checks if it passes, clearing the failure tracking if so + * + * Even on a success, the readiness check will continue to be run until all metrics pass at the same time in order + * to catch cases where a slow request of one metric can trigger calls for other metrics that were previously + * thought ready + * + * @return bool + * @throws UnexpectedAlertOpenException + */ + public function runCheck() { + if ($this->doesMetricPass($this->getCurrentValue(true))) { + $this->setTracker($this->getCurrentValue()); + return true; + } + + return false; + } + + /** + * Update the state of the metric including tracked failure state and checking if a failing value is stuck and + * needs to be reset so future checks can be accurate + * + * Called when the readiness check is finished (either all metrics pass or the check has timed out) + * + * @param Step $step + * @return void + */ + public function finalize($step) { + try { + $currentValue = $this->getCurrentValue(); + } + catch (UnexpectedAlertOpenException $exception) { + $this->debugLog( + 'An alert is open, bypassing javascript-based metric check', + ['action' => $step->getAction()] + ); + return; + } + + if ($this->doesMetricPass($currentValue)) { + $this->setTracker($currentValue); + } + else { + // If failure happened on the same value as before, increment the fail count, otherwise set at 1 + if ($currentValue !== $this->getStoredValue()) { + $failCount = 1; + } + else { + $failCount = $this->getFailureCount() + 1; + } + $this->setTracker($currentValue, $failCount); + + $this->errorLog('Failed readiness check', ['action' => $step->getAction()]); + + if ($this->resetFailureThreshold >= 0 && $failCount >= $this->resetFailureThreshold) { + $this->debugLog( + 'Too many failures, assuming metric is stuck and resetting state', + ['action' => $step->getAction()] + ); + $this->resetMetric(); + } + } + } + + /** + * Helper function to retrieve the driver being used to run the test + * + * @return WebDriver + * @throws ModuleRequireException + */ + protected function getDriver() { + return $this->extension->getDriver(); + } + + /** + * Helper function to execute javascript code, see WebDriver::executeJs for more information + * + * @param string $script + * @param array $arguments + * @return mixed + * @throws UnexpectedAlertOpenException + * @throws ModuleRequireException + */ + protected function executeJs($script, $arguments = []) { + return $this->getDriver()->executeJS($script, $arguments); + } + + /** + * Gets the current state of the given variable + * Fetches an updated value if not known or $refresh is true + * + * @param bool $refresh + * @return mixed + * @throws UnexpectedAlertOpenException + */ + private function getCurrentValue($refresh = false) { + if ($refresh) { + unset($this->currentValue); + } + if (!isset($this->currentValue)) { + $this->currentValue = $this->fetchValueFromPage(); + } + return $this->currentValue; + } + + /** + * Returns the value of the given variable for the previous check + * + * @return mixed + */ + public function getStoredValue() { + return $this->test->getMetadata()->getCurrent($this->getName()); + } + + /** + * The current count of sequential identical failures + * Used to detect potentially stuck metrics + * + * @return int + */ + public function getFailureCount() { + return $this->test->getMetadata()->getCurrent($this->getName() . '.failCount'); + } + + /** + * Update the state of the page to pass the metric and clear the saved failure state + * Called when a failure is found to be stuck + * + * @return void + */ + private function resetMetric() { + $this->clearFailureOnPage(); + $this->setTracker(); + } + + /** + * Tracks the most recent value and the number of identical failures in a row + * + * @param mixed $value + * @param int $failCount + * @return void + */ + public function setTracker($value = null, $failCount = 0) { + $this->test->getMetadata()->setCurrent([ + $this->getName() => $value, + $this->getName() . '.failCount' => $failCount + ]); + unset ($this->currentValue); + } + + /** + * Log the given message to logger->error including context information + * + * @param string $message + * @param array $context + */ + protected function errorLog($message, $context = []) { + $context = array_merge($this->getLogContext(), $context); + $this->logger->error($message, $context); + } + + /** + * Log the given message to logger->info including context information + * + * @param string $message + * @param array $context + */ + protected function infoLog($message, $context = []) { + $context = array_merge($this->getLogContext(), $context); + $this->logger->info($message, $context); + } + + /** + * If verbose, log the given message to logger->debug including context information + * + * @param string $message + * @param array $context + */ + protected function debugLog($message, $context = []) { + if ($this->verbose) { + $context = array_merge($this->getLogContext(), $context); + $this->logger->debug($message, $context); + } + } + + /** + * Base context information to include in all log messages: test name, current URI, metric state + * Reports most recent stored value, not current value, so call setTracker() first to update + * + * @return array + */ + private function getLogContext() { + $testMeta = $this->test->getMetadata(); + return [ + 'test' => $testMeta->getName(), + 'uri' => $testMeta->getCurrent('uri'), + $this->getName() => $this->getStoredValue(), + $this->getName() . '.failCount' => $this->getFailureCount() + ]; + } +} diff --git a/src/Magento/FunctionalTestingFramework/Extension/ReadinessMetrics/DocumentReadyState.php b/src/Magento/FunctionalTestingFramework/Extension/ReadinessMetrics/DocumentReadyState.php new file mode 100644 index 000000000..cf2a82835 --- /dev/null +++ b/src/Magento/FunctionalTestingFramework/Extension/ReadinessMetrics/DocumentReadyState.php @@ -0,0 +1,45 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\FunctionalTestingFramework\Extension\ReadinessMetrics; + +/** + * Class DocumentReadyState + */ + +use Codeception\TestInterface; +use Facebook\WebDriver\Exception\UnexpectedAlertOpenException; +use Magento\FunctionalTestingFramework\Extension\PageReadinessExtension; + +/** + * Class DocumentReadyState + * + * Looks for document.readyState == 'complete' before passing the readiness check + */ +class DocumentReadyState extends AbstractMetricCheck +{ + /** + * Metric passes when document.readyState == 'complete' + * + * @param string $value + * @return bool + */ + protected function doesMetricPass($value) + { + return $value === 'complete'; + } + + /** + * Retrieve document.readyState + * + * @return string + * @throws UnexpectedAlertOpenException + */ + protected function fetchValueFromPage() + { + return $this->executeJS('return document.readyState;'); + } +} diff --git a/src/Magento/FunctionalTestingFramework/Extension/ReadinessMetrics/JQueryAjaxRequests.php b/src/Magento/FunctionalTestingFramework/Extension/ReadinessMetrics/JQueryAjaxRequests.php new file mode 100644 index 000000000..f02574bee --- /dev/null +++ b/src/Magento/FunctionalTestingFramework/Extension/ReadinessMetrics/JQueryAjaxRequests.php @@ -0,0 +1,57 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\FunctionalTestingFramework\Extension\ReadinessMetrics; + +use Facebook\WebDriver\Exception\UnexpectedAlertOpenException; + +/** + * Class JQueryAjaxRequests + * + * Looks for all active jQuery ajax requests to finish before passing the readiness check + */ +class JQueryAjaxRequests extends AbstractMetricCheck +{ + /** + * Metric passes once there are no remaining active requests + * + * @param int $value + * @return bool + */ + protected function doesMetricPass($value) + { + return $value == 0; + } + + /** + * Grabs the number of active jQuery ajax requests if available + * + * @return int + * @throws UnexpectedAlertOpenException + */ + protected function fetchValueFromPage() { + return intval( + $this->executeJS( + 'if (!!window.jQuery) { + return window.jQuery.active; + } + return 0;' + ) + ); + } + + /** + * Active request count can get stuck above zero if an exception is thrown during a callback, causing the + * ajax handler method to fail before decrementing the request count + * + * @return void + * @throws UnexpectedAlertOpenException + */ + protected function clearFailureOnPage() + { + $this->executeJS('if (!!window.jQuery) { window.jQuery.active = 0; };'); + } +} diff --git a/src/Magento/FunctionalTestingFramework/Extension/ReadinessMetrics/MagentoLoadingMasks.php b/src/Magento/FunctionalTestingFramework/Extension/ReadinessMetrics/MagentoLoadingMasks.php new file mode 100644 index 000000000..234a6cd0d --- /dev/null +++ b/src/Magento/FunctionalTestingFramework/Extension/ReadinessMetrics/MagentoLoadingMasks.php @@ -0,0 +1,56 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\FunctionalTestingFramework\Extension\ReadinessMetrics; + +use Codeception\TestInterface; +use Facebook\WebDriver\Exception\NoSuchElementException; +use Facebook\WebDriver\Exception\StaleElementReferenceException; +use Magento\FunctionalTestingFramework\Extension\PageReadinessExtension; +use Magento\FunctionalTestingFramework\Module\MagentoWebDriver; +use WebDriverBy; + +/** + * Class MagentoLoadingMasks + * + * Looks for all loading masks to disappear before passing the readiness check + */ +class MagentoLoadingMasks extends AbstractMetricCheck +{ + /** + * Metric passes once all loading masks are absent or invisible + * + * @param string|null $value + * @return bool + */ + protected function doesMetricPass($value) + { + return is_null($value); + } + + /** + * Get the locator and ID for the first active loading mask or null if there are none visible + * + * @return string|null + */ + protected function fetchValueFromPage() + { + foreach (MagentoWebDriver::$loadingMasksLocators as $maskLocator) { + $driverLocator = WebDriverBy::xpath($maskLocator); + $maskElements = $this->getDriver()->webDriver->findElements($driverLocator); + foreach ($maskElements as $element) { + try { + if ($element->isDisplayed()) { + return "$maskLocator : " . $element ->getID(); + } + } + catch (NoSuchElementException $e) {} + catch (StaleElementReferenceException $e) {} + } + } + return null; + } +} diff --git a/src/Magento/FunctionalTestingFramework/Extension/ReadinessMetrics/PrototypeAjaxRequests.php b/src/Magento/FunctionalTestingFramework/Extension/ReadinessMetrics/PrototypeAjaxRequests.php new file mode 100644 index 000000000..5db1a2d46 --- /dev/null +++ b/src/Magento/FunctionalTestingFramework/Extension/ReadinessMetrics/PrototypeAjaxRequests.php @@ -0,0 +1,57 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\FunctionalTestingFramework\Extension\ReadinessMetrics; + +use Facebook\WebDriver\Exception\UnexpectedAlertOpenException; + +/** + * Class PrototypeAjaxRequests + * + * Looks for all active prototype ajax requests to finish before passing the readiness check + */ +class PrototypeAjaxRequests extends AbstractMetricCheck +{ + /** + * Metric passes once there are no remaining active requests + * + * @param int $value + * @return bool + */ + protected function doesMetricPass($value) + { + return $value == 0; + } + + /** + * Grabs the number of active prototype ajax requests if available + * + * @return int + * @throws UnexpectedAlertOpenException + */ + protected function fetchValueFromPage() { + return intval( + $this->executeJS( + 'if (!!window.Prototype) { + return window.Ajax.activeRequestCount; + } + return 0;' + ) + ); + } + + /** + * Active request count can get stuck above zero if an exception is thrown during a callback, causing the + * ajax handler method to fail before decrementing the request count + * + * @return void + * @throws UnexpectedAlertOpenException + */ + protected function clearFailureOnPage() + { + $this->executeJS('if (!!window.Prototype) { window.Ajax.activeRequestCount = 0; };'); + } +} diff --git a/src/Magento/FunctionalTestingFramework/Extension/ReadinessMetrics/RequireJsDefinitions.php b/src/Magento/FunctionalTestingFramework/Extension/ReadinessMetrics/RequireJsDefinitions.php new file mode 100644 index 000000000..848b55cd0 --- /dev/null +++ b/src/Magento/FunctionalTestingFramework/Extension/ReadinessMetrics/RequireJsDefinitions.php @@ -0,0 +1,62 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\FunctionalTestingFramework\Extension\ReadinessMetrics; + +use Codeception\TestInterface; +use Facebook\WebDriver\Exception\UnexpectedAlertOpenException; +use Magento\FunctionalTestingFramework\Extension\PageReadinessExtension; + +/** + * Class RequireJsDefinitions + * + * Looks for all active require.js module definitions to complete before passing the readiness check + */ +class RequireJsDefinitions extends AbstractMetricCheck +{ + /** + * Metric passes once there are no enabled modules waiting in the registry queue + * + * @param string|null $value + * @return bool + */ + protected function doesMetricPass($value) + { + return is_null($value); + } + + /** + * Retrieve the name of the first enabled module still waiting in the require.js registry queue + * + * @return string|null + * @throws UnexpectedAlertOpenException + */ + protected function fetchValueFromPage() + { + $script = + 'if (!window.requirejs) { + return null; + } + var contexts = window.requirejs.s.contexts; + for (var label in contexts) { + if (contexts.hasOwnProperty(label)) { + var registry = contexts[label].registry; + for (var module in registry) { + if (registry.hasOwnProperty(module) && registry[module].enabled) { + return module; + } + } + } + } + return null;'; + + $moduleInProgress = $this->executeJS($script); + if ($moduleInProgress === 'null') { + $moduleInProgress = null; + } + return $moduleInProgress; + } +} From 8353881c9726a727ee93f8e29b0fdad0cee8e566 Mon Sep 17 00:00:00 2001 From: Alex Calandra <acalandra@magento.com> Date: Tue, 3 Jul 2018 13:13:52 -0500 Subject: [PATCH 096/111] MQE-1068: Require Issue ID for Skipped Test - Removing Skip and description --- .../verification/TestModule/Test/SkippedTest.xml | 4 ++-- .../TestModuleMerged/Test/MergeFunctionalTest.xml | 2 +- .../Test/etc/mergedTestSchema.xsd | 11 ----------- 3 files changed, 3 insertions(+), 14 deletions(-) diff --git a/dev/tests/verification/TestModule/Test/SkippedTest.xml b/dev/tests/verification/TestModule/Test/SkippedTest.xml index 72334a573..bceb619f8 100644 --- a/dev/tests/verification/TestModule/Test/SkippedTest.xml +++ b/dev/tests/verification/TestModule/Test/SkippedTest.xml @@ -14,7 +14,7 @@ <title value="skippedTest"/> <description value=""/> <severity value="AVERAGE"/> - <skip reason="CORE_ISSUE" description="Information"> + <skip> <issueId value="SkippedValue"/> </skip> </annotations> @@ -25,7 +25,7 @@ <title value="skippedMultipleIssuesTest"/> <description value=""/> <severity value="AVERAGE"/> - <skip reason="MFTF_ISSUE"> + <skip> <issueId value="SkippedValue"/> <issueId value="SecondSkippedValue"/> </skip> diff --git a/dev/tests/verification/TestModuleMerged/Test/MergeFunctionalTest.xml b/dev/tests/verification/TestModuleMerged/Test/MergeFunctionalTest.xml index ce8b64d63..912854cc4 100644 --- a/dev/tests/verification/TestModuleMerged/Test/MergeFunctionalTest.xml +++ b/dev/tests/verification/TestModuleMerged/Test/MergeFunctionalTest.xml @@ -28,7 +28,7 @@ </test> <test name="MergeSkip"> <annotations> - <skip reason="UNKNOWN" description="Short Explanation"> + <skip> <issueId value="Issue5"/> </skip> </annotations> diff --git a/src/Magento/FunctionalTestingFramework/Test/etc/mergedTestSchema.xsd b/src/Magento/FunctionalTestingFramework/Test/etc/mergedTestSchema.xsd index 51b2eca6f..b65843bae 100644 --- a/src/Magento/FunctionalTestingFramework/Test/etc/mergedTestSchema.xsd +++ b/src/Magento/FunctionalTestingFramework/Test/etc/mergedTestSchema.xsd @@ -42,15 +42,6 @@ <xs:enumeration value="CRITICAL"/> </xs:restriction> </xs:simpleType> - <xs:simpleType name="skippedReasonEnum" final="restriction"> - <xs:restriction base="xs:string"> - <xs:enumeration value="CORE_ISSUE"/> - <xs:enumeration value="MFTF_ISSUE"/> - <xs:enumeration value="UNKNOWN"/> - <xs:enumeration value="OTHER"/> - <xs:enumeration value="NONE"/> - </xs:restriction> - </xs:simpleType> <xs:complexType name="annotationType"> <xs:simpleContent> <xs:extension base="xs:string"> @@ -69,8 +60,6 @@ <xs:sequence maxOccurs="unbounded"> <xs:element type="issueType" name="issueId" maxOccurs="unbounded"/> </xs:sequence> - <xs:attribute type="skippedReasonEnum" name="reason" use="required"/> - <xs:attribute type="xs:string" name="description"/> </xs:complexType> <xs:complexType name="issueType"> <xs:simpleContent> From e82968721680269444c1d713f4fb8524dbaf5997 Mon Sep 17 00:00:00 2001 From: Kevin Kozan <kkozan@magento.com> Date: Thu, 5 Jul 2018 11:00:23 -0500 Subject: [PATCH 097/111] MQE-1089: Allow usage of $ JavaScript variables in executeJs - Added conditional in testGenerator to escape javascript variables only in executeJS functions. --- .../Resources/ExecuteJsEscapingTest.txt | 35 +++++++++++++++++++ .../TestModule/Test/ExecuteJsTest.xml | 15 ++++++++ .../verification/Tests/ExecuteJsTest.php | 22 ++++++++++++ .../Util/TestGenerator.php | 4 +++ 4 files changed, 76 insertions(+) create mode 100644 dev/tests/verification/Resources/ExecuteJsEscapingTest.txt create mode 100644 dev/tests/verification/TestModule/Test/ExecuteJsTest.xml create mode 100644 dev/tests/verification/Tests/ExecuteJsTest.php diff --git a/dev/tests/verification/Resources/ExecuteJsEscapingTest.txt b/dev/tests/verification/Resources/ExecuteJsEscapingTest.txt new file mode 100644 index 000000000..94dc2cbdb --- /dev/null +++ b/dev/tests/verification/Resources/ExecuteJsEscapingTest.txt @@ -0,0 +1,35 @@ +<?php +namespace Magento\AcceptanceTest\_default\Backend; + +use Magento\FunctionalTestingFramework\AcceptanceTester; +use Magento\FunctionalTestingFramework\DataGenerator\Handlers\DataObjectHandler; +use Magento\FunctionalTestingFramework\DataGenerator\Persist\DataPersistenceHandler; +use Magento\FunctionalTestingFramework\DataGenerator\Objects\EntityDataObject; +use Magento\FunctionalTestingFramework\DataGenerator\Handlers\CredentialStore; +use \Codeception\Util\Locator; +use Yandex\Allure\Adapter\Annotation\Features; +use Yandex\Allure\Adapter\Annotation\Stories; +use Yandex\Allure\Adapter\Annotation\Title; +use Yandex\Allure\Adapter\Annotation\Description; +use Yandex\Allure\Adapter\Annotation\Parameter; +use Yandex\Allure\Adapter\Annotation\Severity; +use Yandex\Allure\Adapter\Model\SeverityLevel; +use Yandex\Allure\Adapter\Annotation\TestCaseId; + +/** + */ +class ExecuteJsEscapingTestCest +{ + /** + * @Features({"TestModule"}) + * @Parameter(name = "AcceptanceTester", value="$I") + * @param AcceptanceTester $I + * @return void + * @throws \Exception + */ + public function ExecuteJsEscapingTest(AcceptanceTester $I) + { + $javaVariableEscape = $I->executeJS("return \$javascriptVariable"); + $mftfVariableNotEscaped = $I->executeJS("return {$doNotEscape}"); + } +} diff --git a/dev/tests/verification/TestModule/Test/ExecuteJsTest.xml b/dev/tests/verification/TestModule/Test/ExecuteJsTest.xml new file mode 100644 index 000000000..2429fa484 --- /dev/null +++ b/dev/tests/verification/TestModule/Test/ExecuteJsTest.xml @@ -0,0 +1,15 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="../../../../../src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + <test name="ExecuteJsEscapingTest"> + <executeJS function="return $javascriptVariable" stepKey="javaVariableEscape"/> + <executeJS function="return {$doNotEscape}" stepKey="mftfVariableNotEscaped"/> + </test> +</tests> diff --git a/dev/tests/verification/Tests/ExecuteJsTest.php b/dev/tests/verification/Tests/ExecuteJsTest.php new file mode 100644 index 000000000..67a5f3c6f --- /dev/null +++ b/dev/tests/verification/Tests/ExecuteJsTest.php @@ -0,0 +1,22 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +namespace tests\verification\Tests; + +use tests\util\MftfTestCase; + +class ExecuteJsTest extends MftfTestCase +{ + /** + * Tests escaping of $javascriptVariable => \$javascriptVariable in the executeJs function + * + * @throws \Exception + * @throws \Magento\FunctionalTestingFramework\Exceptions\TestReferenceException + */ + public function testExecuteJsTest() + { + $this->generateAndCompareTest('ExecuteJsEscapingTest'); + } +} diff --git a/src/Magento/FunctionalTestingFramework/Util/TestGenerator.php b/src/Magento/FunctionalTestingFramework/Util/TestGenerator.php index 5c2b18d1f..3929bf041 100644 --- a/src/Magento/FunctionalTestingFramework/Util/TestGenerator.php +++ b/src/Magento/FunctionalTestingFramework/Util/TestGenerator.php @@ -618,6 +618,10 @@ public function generateStepsPhp($actionObjects, $hookObject = false, $actor = " // Argument must be a closure function, not a string. $function = trim($function, '"'); } + // turn $javaVariable => \$javaVariable but not {$mftfVariable} + if ($actionObject->getType() == "executeJS") { + $function = preg_replace('/(?<!{)(\$[\w\d_]+)/', '\\\\$1', $function); + } } if (isset($customActionAttributes['html'])) { From 62b65400989c5a975f2f8ef831971b157b045495 Mon Sep 17 00:00:00 2001 From: Kevin Kozan <kkozan@magento.com> Date: Fri, 6 Jul 2018 08:15:40 -0500 Subject: [PATCH 098/111] MQE-1087: Test _after hook does not run if test fails with exception - TestContextExtension now handles error events, and runs _after block if the test errored out. --- .../Extension/TestContextExtension.php | 66 +++++++++++++++---- 1 file changed, 53 insertions(+), 13 deletions(-) diff --git a/src/Magento/FunctionalTestingFramework/Extension/TestContextExtension.php b/src/Magento/FunctionalTestingFramework/Extension/TestContextExtension.php index b51ebda0f..c0bf78f19 100644 --- a/src/Magento/FunctionalTestingFramework/Extension/TestContextExtension.php +++ b/src/Magento/FunctionalTestingFramework/Extension/TestContextExtension.php @@ -23,7 +23,8 @@ class TestContextExtension extends \Codeception\Extension */ public static $events = [ Events::TEST_FAIL => 'testFail', - Events::STEP_AFTER => 'afterStep' + Events::STEP_AFTER => 'afterStep', + Events::TEST_END => 'testError' ]; /** @@ -38,22 +39,61 @@ public function testFail(\Codeception\Event\FailEvent $e) // Do not attempt to run _after if failure was in the _after block // Try to run _after but catch exceptions to prevent them from overwriting original failure. if ($context != TestContextExtension::TEST_PHASE_AFTER) { - try { - $actorClass = $e->getTest()->getMetadata()->getCurrent('actor'); - $I = new $actorClass($cest->getScenario()); - call_user_func(\Closure::bind( - function () use ($cest, $I) { - $cest->executeHook($I, 'after'); - }, - null, - $cest - )); - } catch (\Exception $e) { - // Do not rethrow Exception + $this->runAfterBlock($e, $cest); + } + } + + /** + * Codeception event listener function, triggered on test error. + * @param \Codeception\Event\TestEvent $e + * @return void + */ + public function testError(\Codeception\Event\TestEvent $e) + { + $cest = $e->getTest(); + + //Access private TestResultObject to find stack and if there are any errors (as opposed to failures) + $testResultObject = call_user_func(\Closure::bind( + function () use ($cest) { + return $cest->getTestResultObject(); + }, + $cest + )); + $errors = $testResultObject->errors(); + if (!empty($errors)) { + $stack = $errors[0]->thrownException()->getTrace(); + $context = $this->extractContext($stack, $cest->getTestMethod()); + // Do not attempt to run _after if failure was in the _after block + // Try to run _after but catch exceptions to prevent them from overwriting original failure. + if ($context != TestContextExtension::TEST_PHASE_AFTER) { + $this->runAfterBlock($e, $cest); } } } + /** + * Runs cest's after block, if necessary. + * @param Symfony\Component\EventDispatcher\Event $e + * @param \Codeception\TestInterface $cest + * @return void + */ + private function runAfterBlock($e, $cest) + { + try { + $actorClass = $e->getTest()->getMetadata()->getCurrent('actor'); + $I = new $actorClass($cest->getScenario()); + call_user_func(\Closure::bind( + function () use ($cest, $I) { + $cest->executeHook($I, 'after'); + }, + null, + $cest + )); + } catch (\Exception $e) { + // Do not rethrow Exception + } + } + /** * Extracts hook method from trace, looking specifically for the cest class given. * @param array $trace From 8d7cfe8c7cf7c2dcce8f703219adc170394f0816 Mon Sep 17 00:00:00 2001 From: pdohogne-magento <pdohogne@magento.com> Date: Mon, 9 Jul 2018 11:56:50 -0500 Subject: [PATCH 099/111] MAGETWO-46837: Fixing static test failures in readiness extension --- .../Extension/PageReadinessExtension.php | 40 ++++---- .../ReadinessMetrics/AbstractMetricCheck.php | 95 +++++++++++-------- .../ReadinessMetrics/DocumentReadyState.php | 2 +- .../ReadinessMetrics/JQueryAjaxRequests.php | 9 +- .../ReadinessMetrics/MagentoLoadingMasks.php | 8 +- .../PrototypeAjaxRequests.php | 9 +- .../ReadinessMetrics/RequireJsDefinitions.php | 4 +- 7 files changed, 92 insertions(+), 75 deletions(-) diff --git a/src/Magento/FunctionalTestingFramework/Extension/PageReadinessExtension.php b/src/Magento/FunctionalTestingFramework/Extension/PageReadinessExtension.php index f288c6f4e..006e38db1 100644 --- a/src/Magento/FunctionalTestingFramework/Extension/PageReadinessExtension.php +++ b/src/Magento/FunctionalTestingFramework/Extension/PageReadinessExtension.php @@ -44,7 +44,7 @@ class PageReadinessExtension extends Extension /** * Logger verbosity * - * @var bool + * @var boolean */ private $verbose; @@ -91,13 +91,13 @@ public function getDriver() * @param \Codeception\Event\TestEvent $e * @return void */ - public function beforeTest(TestEvent $e) { + public function beforeTest(TestEvent $e) + { $this->test = $e->getTest(); if (isset($this->config['resetFailureThreshold'])) { $failThreshold = intval($this->config['resetFailureThreshold']); - } - else { + } else { $failThreshold = 3; } @@ -116,25 +116,22 @@ public function beforeTest(TestEvent $e) { * @return void * @throws \Exception */ - public function beforeStep(StepEvent $e) { + public function beforeStep(StepEvent $e) + { $step = $e->getStep(); if ($step->getAction() == 'saveScreenshot') { return; } - // $step->getArguments()['skipReadiness'] try { $this->test->getMetadata()->setCurrent(['uri', $this->getDriver()->_getCurrentUri()]); + } catch (\Exception $exception) { + $this->logDebug('Could not retrieve current URI', ['action' => $e->getStep()->getAction()]); } - catch (\Exception $exception) { - // $this->debugLog('Could not retrieve current URI', ['action' => $e->getStep()->getAction()]); - } - if (isset($this->config['timeout'])) { $timeout = intval($this->config['timeout']); - } - else { + } else { $timeout = $this->getDriver()->_getConfig()['pageload_timeout']; } @@ -151,14 +148,14 @@ function () use ($metrics) { if (!$metric->runCheck()) { $passing = false; } + } catch (UnexpectedAlertOpenException $exception) { } - catch (UnexpectedAlertOpenException $exception) {} } return $passing; } ); + } catch (TimeoutException $exception) { } - catch (TimeoutException $exception) {} /** @var AbstractMetricCheck $metric */ foreach ($metrics as $metric) { @@ -172,7 +169,8 @@ function () use ($metrics) { * @param StepEvent $e * @return void */ - public function afterStep(StepEvent $e) { + public function afterStep(StepEvent $e) + { $step = $e->getStep(); if ($step->getAction() == 'saveScreenshot') { return; @@ -180,8 +178,7 @@ public function afterStep(StepEvent $e) { try { $currentUri = $this->getDriver()->_getCurrentUri(); - } - catch (\Exception $e) { + } catch (\Exception $e) { // $this->debugLog('Could not retrieve current URI', ['action' => $step()->getAction()]); return; } @@ -189,7 +186,8 @@ public function afterStep(StepEvent $e) { $previousUri = $this->test->getMetadata()->getCurrent('uri'); if ($previousUri !== $currentUri) { - $this->logDebug('Page URI changed; resetting readiness metric failure tracking', + $this->logDebug( + 'Page URI changed; resetting readiness metric failure tracking', [ 'action' => $step->getAction(), 'newUri' => $currentUri @@ -207,9 +205,11 @@ public function afterStep(StepEvent $e) { * If verbose, log the given message to logger->debug including test context information * * @param string $message - * @param array $context + * @param array $context + * @return void */ - private function logDebug($message, $context = []) { + private function logDebug($message, $context = []) + { if ($this->verbose) { $testMeta = $this->test->getMetadata(); $logContext = [ diff --git a/src/Magento/FunctionalTestingFramework/Extension/ReadinessMetrics/AbstractMetricCheck.php b/src/Magento/FunctionalTestingFramework/Extension/ReadinessMetrics/AbstractMetricCheck.php index 78ba40c4d..65022dc2a 100644 --- a/src/Magento/FunctionalTestingFramework/Extension/ReadinessMetrics/AbstractMetricCheck.php +++ b/src/Magento/FunctionalTestingFramework/Extension/ReadinessMetrics/AbstractMetricCheck.php @@ -55,7 +55,7 @@ abstract class AbstractMetricCheck protected $logger; /** - * @var bool + * @var boolean */ protected $verbose; @@ -63,11 +63,12 @@ abstract class AbstractMetricCheck * Constructor, called from the beforeTest event * * @param PageReadinessExtension $extension - * @param TestInterface $test - * @param integer $resetFailureThreshold + * @param TestInterface $test + * @param integer $resetFailureThreshold * @throws \Exception */ - public function __construct($extension, $test, $resetFailureThreshold){ + public function __construct($extension, $test, $resetFailureThreshold) + { $this->extension = $extension; $this->test = $test; $this->logger = LoggingUtil::getInstance()->getLogger(get_class($this)); @@ -78,8 +79,7 @@ public function __construct($extension, $test, $resetFailureThreshold){ $reflector = new \ReflectionMethod($this, 'clearFailureOnPage'); if ($reflector->getDeclaringClass()->getName() === get_class($this)) { $this->resetFailureThreshold = $resetFailureThreshold; - } - else { + } else { $this->resetFailureThreshold = -1; } @@ -90,9 +90,9 @@ public function __construct($extension, $test, $resetFailureThreshold){ * Does the given value pass the readiness metric * * @param mixed $value - * @return bool + * @return boolean */ - protected abstract function doesMetricPass($value); + abstract protected function doesMetricPass($value); /** * Retrieve the active value for the metric to check from the page @@ -100,7 +100,7 @@ protected abstract function doesMetricPass($value); * @return mixed * @throws UnexpectedAlertOpenException */ - protected abstract function fetchValueFromPage(); + abstract protected function fetchValueFromPage(); /** * Override this method to reset the actual state of the page to make the metric pass @@ -108,7 +108,8 @@ protected abstract function fetchValueFromPage(); * * @return void */ - protected function clearFailureOnPage() { + protected function clearFailureOnPage() + { return; } @@ -117,7 +118,8 @@ protected function clearFailureOnPage() { * * @return string */ - public function getName() { + public function getName() + { $clazz = get_class($this); $namespaceBreak = strrpos($clazz, '\\'); if ($namespaceBreak !== false) { @@ -133,10 +135,11 @@ public function getName() { * to catch cases where a slow request of one metric can trigger calls for other metrics that were previously * thought ready * - * @return bool + * @return boolean * @throws UnexpectedAlertOpenException */ - public function runCheck() { + public function runCheck() + { if ($this->doesMetricPass($this->getCurrentValue(true))) { $this->setTracker($this->getCurrentValue()); return true; @@ -154,11 +157,11 @@ public function runCheck() { * @param Step $step * @return void */ - public function finalize($step) { + public function finalize($step) + { try { $currentValue = $this->getCurrentValue(); - } - catch (UnexpectedAlertOpenException $exception) { + } catch (UnexpectedAlertOpenException $exception) { $this->debugLog( 'An alert is open, bypassing javascript-based metric check', ['action' => $step->getAction()] @@ -168,13 +171,11 @@ public function finalize($step) { if ($this->doesMetricPass($currentValue)) { $this->setTracker($currentValue); - } - else { + } else { // If failure happened on the same value as before, increment the fail count, otherwise set at 1 if ($currentValue !== $this->getStoredValue()) { $failCount = 1; - } - else { + } else { $failCount = $this->getFailureCount() + 1; } $this->setTracker($currentValue, $failCount); @@ -197,7 +198,8 @@ public function finalize($step) { * @return WebDriver * @throws ModuleRequireException */ - protected function getDriver() { + protected function getDriver() + { return $this->extension->getDriver(); } @@ -205,12 +207,13 @@ protected function getDriver() { * Helper function to execute javascript code, see WebDriver::executeJs for more information * * @param string $script - * @param array $arguments + * @param array $arguments * @return mixed * @throws UnexpectedAlertOpenException * @throws ModuleRequireException */ - protected function executeJs($script, $arguments = []) { + protected function executeJs($script, $arguments = []) + { return $this->getDriver()->executeJS($script, $arguments); } @@ -218,11 +221,12 @@ protected function executeJs($script, $arguments = []) { * Gets the current state of the given variable * Fetches an updated value if not known or $refresh is true * - * @param bool $refresh + * @param boolean $refresh * @return mixed * @throws UnexpectedAlertOpenException */ - private function getCurrentValue($refresh = false) { + private function getCurrentValue($refresh = false) + { if ($refresh) { unset($this->currentValue); } @@ -237,7 +241,8 @@ private function getCurrentValue($refresh = false) { * * @return mixed */ - public function getStoredValue() { + public function getStoredValue() + { return $this->test->getMetadata()->getCurrent($this->getName()); } @@ -245,9 +250,10 @@ public function getStoredValue() { * The current count of sequential identical failures * Used to detect potentially stuck metrics * - * @return int + * @return integer */ - public function getFailureCount() { + public function getFailureCount() + { return $this->test->getMetadata()->getCurrent($this->getName() . '.failCount'); } @@ -257,7 +263,8 @@ public function getFailureCount() { * * @return void */ - private function resetMetric() { + private function resetMetric() + { $this->clearFailureOnPage(); $this->setTracker(); } @@ -265,25 +272,28 @@ private function resetMetric() { /** * Tracks the most recent value and the number of identical failures in a row * - * @param mixed $value - * @param int $failCount + * @param mixed $value + * @param integer $failCount * @return void */ - public function setTracker($value = null, $failCount = 0) { + public function setTracker($value = null, $failCount = 0) + { $this->test->getMetadata()->setCurrent([ $this->getName() => $value, $this->getName() . '.failCount' => $failCount ]); - unset ($this->currentValue); + unset($this->currentValue); } /** * Log the given message to logger->error including context information * * @param string $message - * @param array $context + * @param array $context + * @return void */ - protected function errorLog($message, $context = []) { + protected function errorLog($message, $context = []) + { $context = array_merge($this->getLogContext(), $context); $this->logger->error($message, $context); } @@ -292,9 +302,11 @@ protected function errorLog($message, $context = []) { * Log the given message to logger->info including context information * * @param string $message - * @param array $context + * @param array $context + * @return void */ - protected function infoLog($message, $context = []) { + protected function infoLog($message, $context = []) + { $context = array_merge($this->getLogContext(), $context); $this->logger->info($message, $context); } @@ -303,9 +315,11 @@ protected function infoLog($message, $context = []) { * If verbose, log the given message to logger->debug including context information * * @param string $message - * @param array $context + * @param array $context + * @return void */ - protected function debugLog($message, $context = []) { + protected function debugLog($message, $context = []) + { if ($this->verbose) { $context = array_merge($this->getLogContext(), $context); $this->logger->debug($message, $context); @@ -318,7 +332,8 @@ protected function debugLog($message, $context = []) { * * @return array */ - private function getLogContext() { + private function getLogContext() + { $testMeta = $this->test->getMetadata(); return [ 'test' => $testMeta->getName(), diff --git a/src/Magento/FunctionalTestingFramework/Extension/ReadinessMetrics/DocumentReadyState.php b/src/Magento/FunctionalTestingFramework/Extension/ReadinessMetrics/DocumentReadyState.php index cf2a82835..e755fd7c1 100644 --- a/src/Magento/FunctionalTestingFramework/Extension/ReadinessMetrics/DocumentReadyState.php +++ b/src/Magento/FunctionalTestingFramework/Extension/ReadinessMetrics/DocumentReadyState.php @@ -25,7 +25,7 @@ class DocumentReadyState extends AbstractMetricCheck * Metric passes when document.readyState == 'complete' * * @param string $value - * @return bool + * @return boolean */ protected function doesMetricPass($value) { diff --git a/src/Magento/FunctionalTestingFramework/Extension/ReadinessMetrics/JQueryAjaxRequests.php b/src/Magento/FunctionalTestingFramework/Extension/ReadinessMetrics/JQueryAjaxRequests.php index f02574bee..c005923d3 100644 --- a/src/Magento/FunctionalTestingFramework/Extension/ReadinessMetrics/JQueryAjaxRequests.php +++ b/src/Magento/FunctionalTestingFramework/Extension/ReadinessMetrics/JQueryAjaxRequests.php @@ -18,8 +18,8 @@ class JQueryAjaxRequests extends AbstractMetricCheck /** * Metric passes once there are no remaining active requests * - * @param int $value - * @return bool + * @param integer $value + * @return boolean */ protected function doesMetricPass($value) { @@ -29,10 +29,11 @@ protected function doesMetricPass($value) /** * Grabs the number of active jQuery ajax requests if available * - * @return int + * @return integer * @throws UnexpectedAlertOpenException */ - protected function fetchValueFromPage() { + protected function fetchValueFromPage() + { return intval( $this->executeJS( 'if (!!window.jQuery) { diff --git a/src/Magento/FunctionalTestingFramework/Extension/ReadinessMetrics/MagentoLoadingMasks.php b/src/Magento/FunctionalTestingFramework/Extension/ReadinessMetrics/MagentoLoadingMasks.php index 234a6cd0d..abda0cd39 100644 --- a/src/Magento/FunctionalTestingFramework/Extension/ReadinessMetrics/MagentoLoadingMasks.php +++ b/src/Magento/FunctionalTestingFramework/Extension/ReadinessMetrics/MagentoLoadingMasks.php @@ -24,11 +24,11 @@ class MagentoLoadingMasks extends AbstractMetricCheck * Metric passes once all loading masks are absent or invisible * * @param string|null $value - * @return bool + * @return boolean */ protected function doesMetricPass($value) { - return is_null($value); + return $value === null; } /** @@ -46,9 +46,9 @@ protected function fetchValueFromPage() if ($element->isDisplayed()) { return "$maskLocator : " . $element ->getID(); } + } catch (NoSuchElementException $e) { + } catch (StaleElementReferenceException $e) { } - catch (NoSuchElementException $e) {} - catch (StaleElementReferenceException $e) {} } } return null; diff --git a/src/Magento/FunctionalTestingFramework/Extension/ReadinessMetrics/PrototypeAjaxRequests.php b/src/Magento/FunctionalTestingFramework/Extension/ReadinessMetrics/PrototypeAjaxRequests.php index 5db1a2d46..2fc8f70cb 100644 --- a/src/Magento/FunctionalTestingFramework/Extension/ReadinessMetrics/PrototypeAjaxRequests.php +++ b/src/Magento/FunctionalTestingFramework/Extension/ReadinessMetrics/PrototypeAjaxRequests.php @@ -18,8 +18,8 @@ class PrototypeAjaxRequests extends AbstractMetricCheck /** * Metric passes once there are no remaining active requests * - * @param int $value - * @return bool + * @param integer $value + * @return boolean */ protected function doesMetricPass($value) { @@ -29,10 +29,11 @@ protected function doesMetricPass($value) /** * Grabs the number of active prototype ajax requests if available * - * @return int + * @return integer * @throws UnexpectedAlertOpenException */ - protected function fetchValueFromPage() { + protected function fetchValueFromPage() + { return intval( $this->executeJS( 'if (!!window.Prototype) { diff --git a/src/Magento/FunctionalTestingFramework/Extension/ReadinessMetrics/RequireJsDefinitions.php b/src/Magento/FunctionalTestingFramework/Extension/ReadinessMetrics/RequireJsDefinitions.php index 848b55cd0..87f50d118 100644 --- a/src/Magento/FunctionalTestingFramework/Extension/ReadinessMetrics/RequireJsDefinitions.php +++ b/src/Magento/FunctionalTestingFramework/Extension/ReadinessMetrics/RequireJsDefinitions.php @@ -21,11 +21,11 @@ class RequireJsDefinitions extends AbstractMetricCheck * Metric passes once there are no enabled modules waiting in the registry queue * * @param string|null $value - * @return bool + * @return boolean */ protected function doesMetricPass($value) { - return is_null($value); + return $value === null; } /** From bf22ecb886b88d1ee4cb7c362163fbd003edfcee Mon Sep 17 00:00:00 2001 From: Kevin Kozan <kkozan@magento.com> Date: Mon, 9 Jul 2018 13:39:17 -0500 Subject: [PATCH 100/111] MQE-1037: Passing Multiple Element References In Selector Parameters Does Not Generate Correctly When Multiple Elements Are Parameterized - Added negative lookahead for regex. --- dev/tests/verification/Resources/SectionReplacementTest.txt | 2 ++ .../verification/TestModule/Test/SectionReplacementTest.xml | 4 +++- .../FunctionalTestingFramework/Test/Objects/ActionObject.php | 2 +- 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/dev/tests/verification/Resources/SectionReplacementTest.txt b/dev/tests/verification/Resources/SectionReplacementTest.txt index d6afcaa5e..6fdef9083 100644 --- a/dev/tests/verification/Resources/SectionReplacementTest.txt +++ b/dev/tests/verification/Resources/SectionReplacementTest.txt @@ -64,5 +64,7 @@ class SectionReplacementTestCest $I->click("#stringLiteral1-" . $createdData->getCreatedDataByName('firstname') . " .{$data}"); $I->click("#stringLiteral1-" . $createdData->getCreatedDataByName('firstname') . " ." . msq("uniqueData") . "John"); $I->click("#stringLiteral1-" . $createdData->getCreatedDataByName('firstname') . " .Doe" . msq("uniqueData")); + $I->click("#element .1#element .2"); + $I->click("#element .1#element .{$data}"); } } diff --git a/dev/tests/verification/TestModule/Test/SectionReplacementTest.xml b/dev/tests/verification/TestModule/Test/SectionReplacementTest.xml index 93f7e477e..9daef72cd 100644 --- a/dev/tests/verification/TestModule/Test/SectionReplacementTest.xml +++ b/dev/tests/verification/TestModule/Test/SectionReplacementTest.xml @@ -43,10 +43,12 @@ <click stepKey="selectorReplaceThreeParamVariable" selector="{{SampleSection.threeParamElement({$data1}, {$data2}, {$data3})}}"/> <click stepKey="selectorReplaceThreeParamVariableOneDupe" selector="{{SampleSection.threeOneDuplicateParamElement(simpleData.firstname, simpleData.lastname, simpleData.middlename)}}"/> - <click stepKey="selectorReplaceThreeParamMixed1" selector="{{SampleSection.threeParamElement('stringLiteral1', $createdData.firstname$, simpleData.firstname)}}"/> <click stepKey="selectorReplaceThreeParamMixed2" selector="{{SampleSection.threeParamElement('stringLiteral1', $createdData.firstname$, {$data})}}"/> <click stepKey="selectorReplaceThreeParamMixedMSQPrefix" selector="{{SampleSection.threeParamElement('stringLiteral1', $createdData.firstname$, uniqueData.firstname)}}"/> <click stepKey="selectorReplaceThreeParamMixedMSQSuffix" selector="{{SampleSection.threeParamElement('stringLiteral1', $createdData.firstname$, uniqueData.lastname)}}"/> + + <click stepKey="selectorReplaceTwoParamElements" selector="{{SampleSection.oneParamElement('1')}}{{SampleSection.oneParamElement('2')}}"/> + <click stepKey="selectorReplaceTwoParamMixedTypes" selector="{{SampleSection.oneParamElement('1')}}{{SampleSection.oneParamElement({$data})}}"/> </test> </tests> diff --git a/src/Magento/FunctionalTestingFramework/Test/Objects/ActionObject.php b/src/Magento/FunctionalTestingFramework/Test/Objects/ActionObject.php index 302b415d1..f229ccf8a 100644 --- a/src/Magento/FunctionalTestingFramework/Test/Objects/ActionObject.php +++ b/src/Magento/FunctionalTestingFramework/Test/Objects/ActionObject.php @@ -65,7 +65,7 @@ class ActionObject const ACTION_ATTRIBUTE_URL = 'url'; const ACTION_ATTRIBUTE_SELECTOR = 'selector'; const ACTION_ATTRIBUTE_VARIABLE_REGEX_PARAMETER = '/\(.+\)/'; - const ACTION_ATTRIBUTE_VARIABLE_REGEX_PATTERN = '/({{[\w]+\.[\w\[\]]+}})|({{[\w]+\.[\w]+\(.+\)}})/'; + const ACTION_ATTRIBUTE_VARIABLE_REGEX_PATTERN = '/({{[\w]+\.[\w\[\]]+}})|({{[\w]+\.[\w]+\((?(?!}}).)+\)}})/'; /** * The unique identifier for the action From cb1730047c18108c364d8f58ab59fb9289c375ea Mon Sep 17 00:00:00 2001 From: Alex Calandra <acalandra@magento.com> Date: Tue, 10 Jul 2018 08:34:18 -0500 Subject: [PATCH 101/111] MQE-1068: Require Issue ID for Skipped Test - Correcting static check issue --- .../Test/Util/AnnotationExtractor.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Magento/FunctionalTestingFramework/Test/Util/AnnotationExtractor.php b/src/Magento/FunctionalTestingFramework/Test/Util/AnnotationExtractor.php index 65b4b8adb..805a010c2 100644 --- a/src/Magento/FunctionalTestingFramework/Test/Util/AnnotationExtractor.php +++ b/src/Magento/FunctionalTestingFramework/Test/Util/AnnotationExtractor.php @@ -164,7 +164,7 @@ public function validateStoryTitleUniqueness() /** * Validates that all issueId tags contain a non-empty value - * @param array $issues + * @param array $issues * @param string $filename * @throws XmlException * @return void From e68c41d9f9dc5ecc29b1564d4e6a3a18b843abd2 Mon Sep 17 00:00:00 2001 From: Kevin Kozan <kkozan@magento.com> Date: Wed, 11 Jul 2018 08:23:14 -0500 Subject: [PATCH 102/111] MQE-1047: magentoCLI command does not work if MAGENTO_BASE_URL uses index.php - apiURL now tries to trim index.php/ if present and all others after first / in url. --- .../FunctionalTestingFramework/Module/MagentoWebDriver.php | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/Magento/FunctionalTestingFramework/Module/MagentoWebDriver.php b/src/Magento/FunctionalTestingFramework/Module/MagentoWebDriver.php index f1aae2487..62907fca4 100644 --- a/src/Magento/FunctionalTestingFramework/Module/MagentoWebDriver.php +++ b/src/Magento/FunctionalTestingFramework/Module/MagentoWebDriver.php @@ -457,7 +457,11 @@ public function scrollToTopOfPage() */ public function magentoCLI($command, $arguments = null) { - $apiURL = $this->config['url'] . getenv('MAGENTO_CLI_COMMAND_PATH'); + // trim everything after first '/' in URL after (ex http://magento.instance/<index.php>) + preg_match("/.+\/\/[^\/]+\/?/", $this->config['url'], $trimmed); + $trimmedUrl = $trimmed[0]; + $apiURL = $trimmedUrl . ltrim(getenv('MAGENTO_CLI_COMMAND_PATH'), '/'); + $executor = new CurlTransport(); $executor->write( $apiURL, From 890b3920bd451d80b5f48595f0415ad03ebd5941 Mon Sep 17 00:00:00 2001 From: Kevin Kozan <kkozan@magento.com> Date: Thu, 12 Jul 2018 15:20:43 -0500 Subject: [PATCH 103/111] MQE-1110: MFTF run:group does not work with a standalone configuration - added require_once of proper bootstrap for codeception execution. --- dev/tests/functional/_bootstrap.php | 7 ++++++ .../Console/RunTestCommand.php | 15 +++++++++---- .../Console/RunTestGroupCommand.php | 22 +++++++++++++++++-- .../Util/ModuleResolver.php | 15 ++++++++++--- .../FunctionalTestingFramework/_bootstrap.php | 3 ++- 5 files changed, 52 insertions(+), 10 deletions(-) diff --git a/dev/tests/functional/_bootstrap.php b/dev/tests/functional/_bootstrap.php index 5e6621fe0..219a2400f 100755 --- a/dev/tests/functional/_bootstrap.php +++ b/dev/tests/functional/_bootstrap.php @@ -4,6 +4,13 @@ * See COPYING.txt for license details. */ +//Do not continue running this bootstrap if PHPUnit is calling it +$fullTrace = debug_backtrace(); +$rootFile = array_values(array_slice($fullTrace, -1))[0]['file']; +if (strpos($rootFile, "phpunit") !== false) { + return; +} + defined('PROJECT_ROOT') || define('PROJECT_ROOT', dirname(dirname(dirname(__DIR__)))); require_once realpath(PROJECT_ROOT . '/vendor/autoload.php'); diff --git a/src/Magento/FunctionalTestingFramework/Console/RunTestCommand.php b/src/Magento/FunctionalTestingFramework/Console/RunTestCommand.php index 3c06a8ff4..9bf250ed0 100644 --- a/src/Magento/FunctionalTestingFramework/Console/RunTestCommand.php +++ b/src/Magento/FunctionalTestingFramework/Console/RunTestCommand.php @@ -31,7 +31,13 @@ protected function configure() 'name', InputArgument::REQUIRED | InputArgument::IS_ARRAY, "name of tests to generate and execute" - )->addOption('skip-generate', 'k', InputOption::VALUE_NONE, "skip generation and execute existing test"); + )->addOption('skip-generate', 'k', InputOption::VALUE_NONE, "skip generation and execute existing test") + ->addOption( + "force", + 'f', + InputOption::VALUE_NONE, + 'force generation of tests regardless of Magento Instance Configuration' + ); } /** @@ -47,7 +53,8 @@ protected function configure() protected function execute(InputInterface $input, OutputInterface $output) { $tests = $input->getArgument('name'); - $skipGeneration = $input->getOption('skip-generate') ?? false; + $skipGeneration = $input->getOption('skip-generate'); + $force = $input->getOption('force'); if (!$skipGeneration) { $command = $this->getApplication()->find('generate:tests'); @@ -55,9 +62,9 @@ protected function execute(InputInterface $input, OutputInterface $output) '--tests' => json_encode([ 'tests' => $tests, 'suites' => null - ]) + ]), + '--force' => $force ]; - $command->run(new ArrayInput($args), $output); } diff --git a/src/Magento/FunctionalTestingFramework/Console/RunTestGroupCommand.php b/src/Magento/FunctionalTestingFramework/Console/RunTestGroupCommand.php index 06e296b58..110b86565 100644 --- a/src/Magento/FunctionalTestingFramework/Console/RunTestGroupCommand.php +++ b/src/Magento/FunctionalTestingFramework/Console/RunTestGroupCommand.php @@ -8,6 +8,7 @@ namespace Magento\FunctionalTestingFramework\Console; use Magento\FunctionalTestingFramework\Suite\Handlers\SuiteObjectHandler; +use Magento\FunctionalTestingFramework\Config\MftfApplicationConfig; use Magento\FunctionalTestingFramework\Test\Handlers\TestObjectHandler; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\ArrayInput; @@ -33,6 +34,11 @@ protected function configure() 'k', InputOption::VALUE_NONE, "only execute a group of tests without generating from source xml" + )->addOption( + "force", + 'f', + InputOption::VALUE_NONE, + 'force generation of tests regardless of Magento Instance Configuration' )->addArgument( 'groups', InputArgument::IS_ARRAY | InputArgument::REQUIRED, @@ -52,13 +58,25 @@ protected function configure() */ protected function execute(InputInterface $input, OutputInterface $output) { - $skipGeneration = $input->getOption('skip-generate') ?? false; + $skipGeneration = $input->getOption('skip-generate'); + $force = $input->getOption('force'); $groups = $input->getArgument('groups'); + // Create Mftf Configuration + MftfApplicationConfig::create( + $force, + MftfApplicationConfig::GENERATION_PHASE, + false, + false + ); + if (!$skipGeneration) { $testConfiguration = $this->getGroupAndSuiteConfiguration($groups); $command = $this->getApplication()->find('generate:tests'); - $args = ['--tests' => $testConfiguration]; + $args = [ + '--tests' => $testConfiguration, + '--force' => $force + ]; $command->run(new ArrayInput($args), $output); } diff --git a/src/Magento/FunctionalTestingFramework/Util/ModuleResolver.php b/src/Magento/FunctionalTestingFramework/Util/ModuleResolver.php index 363d3b9e6..0a5974387 100644 --- a/src/Magento/FunctionalTestingFramework/Util/ModuleResolver.php +++ b/src/Magento/FunctionalTestingFramework/Util/ModuleResolver.php @@ -9,6 +9,7 @@ use Magento\FunctionalTestingFramework\Config\MftfApplicationConfig; use Magento\FunctionalTestingFramework\Exceptions\TestFrameworkException; use Magento\FunctionalTestingFramework\Util\Logger\LoggingUtil; +use Symfony\Component\HttpFoundation\Response; /** * Class ModuleResolver, resolve module path based on enabled modules of target Magento instance. @@ -402,11 +403,19 @@ protected function getAdminToken() curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); $response = curl_exec($ch); + $responseCode = curl_getinfo($ch)['http_code']; - if (!$response) { - $message = "Could not retrieve API token from Magento Instance."; + if ($responseCode !== 200) { + if ($responseCode == 0) { + $details = "Could not find Magento Instance at given MAGENTO_BASE_URL"; + } else { + $details = $responseCode . " " . Response::$statusTexts[$responseCode]; + } + + $message = "Could not retrieve API token from Magento Instance ({$details})"; $context = [ - "Admin Integration Token Url" => $url, + "tokenUrl" => $url, + "responseCode" => $responseCode, "MAGENTO_ADMIN_USERNAME" => getenv("MAGENTO_ADMIN_USERNAME"), "MAGENTO_ADMIN_PASSWORD" => getenv("MAGENTO_ADMIN_PASSWORD"), ]; diff --git a/src/Magento/FunctionalTestingFramework/_bootstrap.php b/src/Magento/FunctionalTestingFramework/_bootstrap.php index 3dd871adc..2e769fb8f 100644 --- a/src/Magento/FunctionalTestingFramework/_bootstrap.php +++ b/src/Magento/FunctionalTestingFramework/_bootstrap.php @@ -12,7 +12,8 @@ $projectRootPath = substr(FW_BP, 0, strpos(FW_BP, DIRECTORY_SEPARATOR . 'vendor' . DIRECTORY_SEPARATOR)); if (empty($projectRootPath)) { - // Currently we do not support global execution, so leave this script before pathing is set improperly + // If ProjectRootPath is empty, we are not under vendor and are executing standalone. + require_once (realpath(FW_BP . "/dev/tests/functional/_bootstrap.php")); return; } From ff467df26965b79d1f38d22a556054c6168cfd98 Mon Sep 17 00:00:00 2001 From: pdohogne-magento <pdohogne@magento.com> Date: Thu, 12 Jul 2018 17:25:25 -0500 Subject: [PATCH 104/111] MAGETWO-46837: Extracting metric tracking from test object and adding wait and comment to ignored action types --- .../Extension/PageReadinessExtension.php | 127 ++++++++++++------ .../ReadinessMetrics/AbstractMetricCheck.php | 72 ++++++---- .../ReadinessMetrics/DocumentReadyState.php | 2 - .../ReadinessMetrics/MagentoLoadingMasks.php | 2 - .../ReadinessMetrics/RequireJsDefinitions.php | 2 - 5 files changed, 128 insertions(+), 77 deletions(-) diff --git a/src/Magento/FunctionalTestingFramework/Extension/PageReadinessExtension.php b/src/Magento/FunctionalTestingFramework/Extension/PageReadinessExtension.php index 006e38db1..393d03644 100644 --- a/src/Magento/FunctionalTestingFramework/Extension/PageReadinessExtension.php +++ b/src/Magento/FunctionalTestingFramework/Extension/PageReadinessExtension.php @@ -12,7 +12,7 @@ use Codeception\Exception\ModuleRequireException; use Codeception\Extension; use Codeception\Module\WebDriver; -use Codeception\TestInterface; +use Codeception\Step; use Facebook\WebDriver\Exception\UnexpectedAlertOpenException; use Magento\FunctionalTestingFramework\Extension\ReadinessMetrics\AbstractMetricCheck; use Facebook\WebDriver\Exception\TimeOutException; @@ -32,8 +32,18 @@ class PageReadinessExtension extends Extension */ public static $events = [ Events::TEST_BEFORE => 'beforeTest', - Events::STEP_BEFORE => 'beforeStep', - Events::STEP_AFTER => 'afterStep' + Events::STEP_BEFORE => 'beforeStep' + ]; + + /** + * List of action types that should bypass metric checks + * shouldSkipCheck() also checks for the 'Comment' step type, which doesn't follow the $step->getAction() pattern + * + * @var array + */ + private $ignoredActions = [ + 'saveScreenshot', + 'wait' ]; /** @@ -56,11 +66,18 @@ class PageReadinessExtension extends Extension private $readinessMetrics; /** - * Active test object + * The name of the active test * - * @var TestInterface + * @var string */ - private $test; + private $testName; + + /** + * The current URI of the active page + * + * @var string + */ + private $uri; /** * Initialize local vars @@ -93,17 +110,18 @@ public function getDriver() */ public function beforeTest(TestEvent $e) { - $this->test = $e->getTest(); - if (isset($this->config['resetFailureThreshold'])) { $failThreshold = intval($this->config['resetFailureThreshold']); } else { $failThreshold = 3; } + $this->testName = $e->getTest()->getMetadata()->getName(); + $this->uri = null; + $metrics = []; foreach ($this->config['readinessMetrics'] as $metricClass) { - $metrics[] = new $metricClass($this, $this->test, $failThreshold); + $metrics[] = new $metricClass($this, $failThreshold); } $this->readinessMetrics = $metrics; @@ -119,16 +137,13 @@ public function beforeTest(TestEvent $e) public function beforeStep(StepEvent $e) { $step = $e->getStep(); - if ($step->getAction() == 'saveScreenshot') { + if ($this->shouldSkipCheck($step)) { return; } - try { - $this->test->getMetadata()->setCurrent(['uri', $this->getDriver()->_getCurrentUri()]); - } catch (\Exception $exception) { - $this->logDebug('Could not retrieve current URI', ['action' => $e->getStep()->getAction()]); - } + $this->checkForNewPage($step); + // todo: Implement step parameter to override global timeout configuration if (isset($this->config['timeout'])) { $timeout = intval($this->config['timeout']); } else { @@ -159,46 +174,75 @@ function () use ($metrics) { /** @var AbstractMetricCheck $metric */ foreach ($metrics as $metric) { - $metric->finalize($step); + $metric->finalizeForStep($step); } } /** - * Checks to see if the step changed the uri and resets failure tracking if so + * Check if the URI has changed and reset metric tracking if so * - * @param StepEvent $e + * @param Step $step * @return void */ - public function afterStep(StepEvent $e) + private function checkForNewPage($step) { - $step = $e->getStep(); - if ($step->getAction() == 'saveScreenshot') { - return; - } - try { $currentUri = $this->getDriver()->_getCurrentUri(); + + if ($this->uri !== $currentUri) { + $this->logDebug( + 'Page URI changed; resetting readiness metric failure tracking', + [ + 'step' => $step->__toString(), + 'newUri' => $currentUri + ] + ); + + /** @var AbstractMetricCheck $metric */ + foreach ($this->readinessMetrics as $metric) { + $metric->resetTracker(); + } + + $this->uri = $currentUri; + } } catch (\Exception $e) { - // $this->debugLog('Could not retrieve current URI', ['action' => $step()->getAction()]); - return; + $this->logDebug('Could not retrieve current URI', ['step' => $step->__toString()]); } + } - $previousUri = $this->test->getMetadata()->getCurrent('uri'); + /** + * Gets the active page URI from the start of the most recent step + * + * @return string + */ + public function getUri() + { + return $this->uri; + } - if ($previousUri !== $currentUri) { - $this->logDebug( - 'Page URI changed; resetting readiness metric failure tracking', - [ - 'action' => $step->getAction(), - 'newUri' => $currentUri - ] - ); + /** + * Gets the name of the active test + * + * @return string + */ + public function getTestName() + { + return $this->testName; + } - /** @var AbstractMetricCheck $metric */ - foreach ($this->readinessMetrics as $metric) { - $metric->setTracker(); - } + /** + * Should the given step bypass the readiness checks + * todo: Implement step parameter to bypass specific metrics (or all) instead of basing on action type + * + * @param Step $step + * @return boolean + */ + private function shouldSkipCheck($step) + { + if ($step instanceof Step\Comment || in_array($step->getAction(), $this->ignoredActions)) { + return true; } + return false; } /** @@ -211,10 +255,9 @@ public function afterStep(StepEvent $e) private function logDebug($message, $context = []) { if ($this->verbose) { - $testMeta = $this->test->getMetadata(); $logContext = [ - 'test' => $testMeta->getName(), - 'uri' => $testMeta->getCurrent('uri') + 'test' => $this->testName, + 'uri' => $this->uri ]; foreach ($this->readinessMetrics as $metric) { $logContext[$metric->getName()] = $metric->getStoredValue(); diff --git a/src/Magento/FunctionalTestingFramework/Extension/ReadinessMetrics/AbstractMetricCheck.php b/src/Magento/FunctionalTestingFramework/Extension/ReadinessMetrics/AbstractMetricCheck.php index 65022dc2a..c5dd7891b 100644 --- a/src/Magento/FunctionalTestingFramework/Extension/ReadinessMetrics/AbstractMetricCheck.php +++ b/src/Magento/FunctionalTestingFramework/Extension/ReadinessMetrics/AbstractMetricCheck.php @@ -9,7 +9,6 @@ use Codeception\Exception\ModuleRequireException; use Codeception\Module\WebDriver; use Codeception\Step; -use Codeception\TestInterface; use Facebook\WebDriver\Exception\UnexpectedAlertOpenException; use Magento\FunctionalTestingFramework\Extension\PageReadinessExtension; use Magento\FunctionalTestingFramework\Util\Logger\LoggingUtil; @@ -29,18 +28,26 @@ abstract class AbstractMetricCheck protected $extension; /** - * The active test object + * Current state of the value the metric tracks * - * @var TestInterface + * @var mixed; */ - protected $test; + protected $currentValue; /** - * Current state of the value the metric tracks + * Most recent saved state of the value the metric tracks + * Updated when the metric passes or is finalized * * @var mixed; */ - protected $currentValue; + protected $storedValue; + + /** + * Current count of sequential identical failures + * + * @var integer; + */ + protected $failCount; /** * Number of sequential identical failures before force-resetting the metric @@ -63,14 +70,12 @@ abstract class AbstractMetricCheck * Constructor, called from the beforeTest event * * @param PageReadinessExtension $extension - * @param TestInterface $test * @param integer $resetFailureThreshold * @throws \Exception */ - public function __construct($extension, $test, $resetFailureThreshold) + public function __construct($extension, $resetFailureThreshold) { $this->extension = $extension; - $this->test = $test; $this->logger = LoggingUtil::getInstance()->getLogger(get_class($this)); $this->verbose = MftfApplicationConfig::getConfig()->verboseEnabled(); @@ -83,7 +88,7 @@ public function __construct($extension, $test, $resetFailureThreshold) $this->resetFailureThreshold = -1; } - $this->setTracker(); + $this->resetTracker(); } /** @@ -141,7 +146,7 @@ public function getName() public function runCheck() { if ($this->doesMetricPass($this->getCurrentValue(true))) { - $this->setTracker($this->getCurrentValue()); + $this->setTracker($this->getCurrentValue(), 0); return true; } @@ -157,35 +162,35 @@ public function runCheck() * @param Step $step * @return void */ - public function finalize($step) + public function finalizeForStep($step) { try { $currentValue = $this->getCurrentValue(); } catch (UnexpectedAlertOpenException $exception) { $this->debugLog( 'An alert is open, bypassing javascript-based metric check', - ['action' => $step->getAction()] + ['step' => $step->__toString()] ); return; } if ($this->doesMetricPass($currentValue)) { - $this->setTracker($currentValue); + $this->setTracker($currentValue, 0); } else { // If failure happened on the same value as before, increment the fail count, otherwise set at 1 - if ($currentValue !== $this->getStoredValue()) { + if (!isset($this->storedValue) || $currentValue !== $this->getStoredValue()) { $failCount = 1; } else { $failCount = $this->getFailureCount() + 1; } $this->setTracker($currentValue, $failCount); - $this->errorLog('Failed readiness check', ['action' => $step->getAction()]); + $this->errorLog('Failed readiness check', ['step' => $step->__toString()]); if ($this->resetFailureThreshold >= 0 && $failCount >= $this->resetFailureThreshold) { $this->debugLog( 'Too many failures, assuming metric is stuck and resetting state', - ['action' => $step->getAction()] + ['step' => $step->__toString()] ); $this->resetMetric(); } @@ -214,7 +219,7 @@ protected function getDriver() */ protected function executeJs($script, $arguments = []) { - return $this->getDriver()->executeJS($script, $arguments); + return $this->extension->getDriver()->executeJS($script, $arguments); } /** @@ -243,7 +248,7 @@ private function getCurrentValue($refresh = false) */ public function getStoredValue() { - return $this->test->getMetadata()->getCurrent($this->getName()); + return $this->storedValue; } /** @@ -254,7 +259,7 @@ public function getStoredValue() */ public function getFailureCount() { - return $this->test->getMetadata()->getCurrent($this->getName() . '.failCount'); + return $this->failCount; } /** @@ -266,7 +271,7 @@ public function getFailureCount() private function resetMetric() { $this->clearFailureOnPage(); - $this->setTracker(); + $this->resetTracker(); } /** @@ -276,13 +281,23 @@ private function resetMetric() * @param integer $failCount * @return void */ - public function setTracker($value = null, $failCount = 0) + public function setTracker($value, $failCount) + { + unset($this->currentValue); + $this->storedValue = $value; + $this->failCount = $failCount; + } + + /** + * Resets the tracked metric values on a new page or stuck failure + * + * @return void + */ + public function resetTracker() { - $this->test->getMetadata()->setCurrent([ - $this->getName() => $value, - $this->getName() . '.failCount' => $failCount - ]); unset($this->currentValue); + unset($this->storedValue); + $this->failCount = 0; } /** @@ -334,10 +349,9 @@ protected function debugLog($message, $context = []) */ private function getLogContext() { - $testMeta = $this->test->getMetadata(); return [ - 'test' => $testMeta->getName(), - 'uri' => $testMeta->getCurrent('uri'), + 'test' => $this->extension->getTestName(), + 'uri' => $this->extension->getUri(), $this->getName() => $this->getStoredValue(), $this->getName() . '.failCount' => $this->getFailureCount() ]; diff --git a/src/Magento/FunctionalTestingFramework/Extension/ReadinessMetrics/DocumentReadyState.php b/src/Magento/FunctionalTestingFramework/Extension/ReadinessMetrics/DocumentReadyState.php index e755fd7c1..26cf91aa7 100644 --- a/src/Magento/FunctionalTestingFramework/Extension/ReadinessMetrics/DocumentReadyState.php +++ b/src/Magento/FunctionalTestingFramework/Extension/ReadinessMetrics/DocumentReadyState.php @@ -10,9 +10,7 @@ * Class DocumentReadyState */ -use Codeception\TestInterface; use Facebook\WebDriver\Exception\UnexpectedAlertOpenException; -use Magento\FunctionalTestingFramework\Extension\PageReadinessExtension; /** * Class DocumentReadyState diff --git a/src/Magento/FunctionalTestingFramework/Extension/ReadinessMetrics/MagentoLoadingMasks.php b/src/Magento/FunctionalTestingFramework/Extension/ReadinessMetrics/MagentoLoadingMasks.php index abda0cd39..4f15524ba 100644 --- a/src/Magento/FunctionalTestingFramework/Extension/ReadinessMetrics/MagentoLoadingMasks.php +++ b/src/Magento/FunctionalTestingFramework/Extension/ReadinessMetrics/MagentoLoadingMasks.php @@ -6,10 +6,8 @@ namespace Magento\FunctionalTestingFramework\Extension\ReadinessMetrics; -use Codeception\TestInterface; use Facebook\WebDriver\Exception\NoSuchElementException; use Facebook\WebDriver\Exception\StaleElementReferenceException; -use Magento\FunctionalTestingFramework\Extension\PageReadinessExtension; use Magento\FunctionalTestingFramework\Module\MagentoWebDriver; use WebDriverBy; diff --git a/src/Magento/FunctionalTestingFramework/Extension/ReadinessMetrics/RequireJsDefinitions.php b/src/Magento/FunctionalTestingFramework/Extension/ReadinessMetrics/RequireJsDefinitions.php index 87f50d118..6df470123 100644 --- a/src/Magento/FunctionalTestingFramework/Extension/ReadinessMetrics/RequireJsDefinitions.php +++ b/src/Magento/FunctionalTestingFramework/Extension/ReadinessMetrics/RequireJsDefinitions.php @@ -6,9 +6,7 @@ namespace Magento\FunctionalTestingFramework\Extension\ReadinessMetrics; -use Codeception\TestInterface; use Facebook\WebDriver\Exception\UnexpectedAlertOpenException; -use Magento\FunctionalTestingFramework\Extension\PageReadinessExtension; /** * Class RequireJsDefinitions From 45d802bb369c72eb41676cd7df475566f2fe723e Mon Sep 17 00:00:00 2001 From: Kevin Kozan <kkozan@magento.com> Date: Sun, 15 Jul 2018 19:43:56 -0500 Subject: [PATCH 105/111] MQE-1141: UpdateTestSchemaPaths broken due to updated relative paths - Updated paths of all relevant paths. --- .../Upgrade/UpdateTestSchemaPaths.php | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/src/Magento/FunctionalTestingFramework/Upgrade/UpdateTestSchemaPaths.php b/src/Magento/FunctionalTestingFramework/Upgrade/UpdateTestSchemaPaths.php index 80592a474..8b79b6019 100644 --- a/src/Magento/FunctionalTestingFramework/Upgrade/UpdateTestSchemaPaths.php +++ b/src/Magento/FunctionalTestingFramework/Upgrade/UpdateTestSchemaPaths.php @@ -24,22 +24,24 @@ class UpdateTestSchemaPaths implements UpgradeInterface */ public function execute(InputInterface $input) { + // @codingStandardsIgnoreStart $relativeToUrn = [ - "/src/Magento/FunctionalTestingFramework/DataGenerator/etc/dataOperation.xsd" + "dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/DataGenerator/etc/dataOperation.xsd" => "urn:magento:mftf:DataGenerator/etc/dataOperation.xsd", - "/src/Magento/FunctionalTestingFramework/DataGenerator/etc/dataProfileSchema.xsd" + "dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/DataGenerator/etc/dataProfileSchema.xsd" => "urn:magento:mftf:DataGenerator/etc/dataProfileSchema.xsd", - "/src/Magento/FunctionalTestingFramework/Page/etc/PageObject.xsd" + "dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/PageObject.xsd" => "urn:magento:mftf:Page/etc/PageObject.xsd", - "/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd" + "dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd" => "urn:magento:mftf:Page/etc/SectionObject.xsd", - "/src/Magento/FunctionalTestingFramework/Test/etc/actionGroupSchema.xsd" + "dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/actionGroupSchema.xsd" => "urn:magento:mftf:Test/etc/actionGroupSchema.xsd", - "/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd" + "dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd" => "urn:magento:mftf:Test/etc/testSchema.xsd", - "/src/Magento/FunctionalTestingFramework/Suite/etc/suiteSchema.xsd" + "dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Suite/etc/suiteSchema.xsd" => "urn:magento:mftf:Suite/etc/suiteSchema.xsd" ]; + // @codingStandardsIgnoreEnd $relativePatterns = []; $urns = []; From 012f56f05aaa5d73feb54b5b25032f2e01a8e6ee Mon Sep 17 00:00:00 2001 From: Kevin Kozan <kkozan@magento.com> Date: Tue, 17 Jul 2018 08:52:40 -0500 Subject: [PATCH 106/111] MQE-917: Eliminate usage of XSD relative paths - Added --force flag if user does not have a misc.xml file. --- .../Console/GenerateDevUrnCommand.php | 26 +++++++++++++++---- 1 file changed, 21 insertions(+), 5 deletions(-) diff --git a/src/Magento/FunctionalTestingFramework/Console/GenerateDevUrnCommand.php b/src/Magento/FunctionalTestingFramework/Console/GenerateDevUrnCommand.php index 43688e702..1147704c0 100644 --- a/src/Magento/FunctionalTestingFramework/Console/GenerateDevUrnCommand.php +++ b/src/Magento/FunctionalTestingFramework/Console/GenerateDevUrnCommand.php @@ -13,6 +13,8 @@ use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Input\InputOption; +use Magento\FunctionalTestingFramework\Util\Logger\LoggingUtil; class GenerateDevUrnCommand extends Command { @@ -25,7 +27,13 @@ protected function configure() { $this->setName('generate:urn-catalog') ->setDescription('This command generates an URN catalog to enable PHPStorm to recognize and highlight URNs.') - ->addArgument('path', InputArgument::REQUIRED, 'path to PHPStorm misc.xml file (typically located in [ProjectRoot]/.idea/misc.xml)'); + ->addArgument('path', InputArgument::REQUIRED, 'path to PHPStorm misc.xml file (typically located in [ProjectRoot]/.idea/misc.xml)') + ->addOption( + "force", + 'f', + InputOption::VALUE_NONE, + 'forces creation of misc.xml file if not found in the path given.' + ); } /** @@ -40,12 +48,20 @@ protected function execute(InputInterface $input, OutputInterface $output) { $miscXmlFilePath = $input->getArgument('path') . DIRECTORY_SEPARATOR . "misc.xml"; $miscXmlFile = realpath($miscXmlFilePath); + $force = $input->getOption('force'); if ($miscXmlFile === false) { - $exceptionMessage = "misc.xml not found in given path '{$miscXmlFilePath}'"; - LoggingUtil::getInstance()->getLogger(GenerateDevUrnCommand::class) - ->error($exceptionMessage); - throw new TestFrameworkException($exceptionMessage); + if ($force == true) { + // create file and refresh realpath + $xml = "<project version=\"4\"/>"; + file_put_contents($miscXmlFilePath, $xml); + $miscXmlFile = realpath($miscXmlFilePath); + } else { + $exceptionMessage = "misc.xml not found in given path '{$miscXmlFilePath}'"; + LoggingUtil::getInstance()->getLogger(GenerateDevUrnCommand::class) + ->error($exceptionMessage); + throw new TestFrameworkException($exceptionMessage); + } } $dom = new \DOMDocument('1.0'); $dom->preserveWhiteSpace = false; From ae109cd653ca4bb4eb3101b7a6d13521dbe5176f Mon Sep 17 00:00:00 2001 From: KevinBKozan <kkozan@magento.com> Date: Wed, 18 Jul 2018 11:34:18 -0500 Subject: [PATCH 107/111] Turning off all logging for Readiness Extension - Causing jenkins failures --- .../Extension/PageReadinessExtension.php | 2 +- .../Extension/ReadinessMetrics/AbstractMetricCheck.php | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Magento/FunctionalTestingFramework/Extension/PageReadinessExtension.php b/src/Magento/FunctionalTestingFramework/Extension/PageReadinessExtension.php index 393d03644..45ed651f1 100644 --- a/src/Magento/FunctionalTestingFramework/Extension/PageReadinessExtension.php +++ b/src/Magento/FunctionalTestingFramework/Extension/PageReadinessExtension.php @@ -264,7 +264,7 @@ private function logDebug($message, $context = []) $logContext[$metric->getName() . '.failCount'] = $metric->getFailureCount(); } $context = array_merge($logContext, $context); - $this->logger->info($message, $context); + //$this->logger->info($message, $context); } } } diff --git a/src/Magento/FunctionalTestingFramework/Extension/ReadinessMetrics/AbstractMetricCheck.php b/src/Magento/FunctionalTestingFramework/Extension/ReadinessMetrics/AbstractMetricCheck.php index c5dd7891b..c78d2ab6f 100644 --- a/src/Magento/FunctionalTestingFramework/Extension/ReadinessMetrics/AbstractMetricCheck.php +++ b/src/Magento/FunctionalTestingFramework/Extension/ReadinessMetrics/AbstractMetricCheck.php @@ -248,7 +248,7 @@ private function getCurrentValue($refresh = false) */ public function getStoredValue() { - return $this->storedValue; + return $this->storedValue ?? null; } /** @@ -310,7 +310,7 @@ public function resetTracker() protected function errorLog($message, $context = []) { $context = array_merge($this->getLogContext(), $context); - $this->logger->error($message, $context); + //$this->logger->error($message, $context); } /** @@ -323,7 +323,7 @@ protected function errorLog($message, $context = []) protected function infoLog($message, $context = []) { $context = array_merge($this->getLogContext(), $context); - $this->logger->info($message, $context); + //$this->logger->info($message, $context); } /** @@ -337,7 +337,7 @@ protected function debugLog($message, $context = []) { if ($this->verbose) { $context = array_merge($this->getLogContext(), $context); - $this->logger->debug($message, $context); + //$this->logger->debug($message, $context); } } From b8ebe86e8e0901eae69b6992ca7bac2bb6f797b9 Mon Sep 17 00:00:00 2001 From: Kevin Kozan <kkozan@magento.com> Date: Wed, 18 Jul 2018 11:40:08 -0500 Subject: [PATCH 108/111] [2.3.0 - Release] Changelog - Added changelog for 2.3.0 --- CHANGELOG.md | 53 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8d403f911..768cad7d3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,59 @@ Magento Functional Testing Framework Changelog ================================================ +2.3.0 +----- +### Enhancements +* Traceability + * MFTF now outputs generation run-time information, warnings, and errors to an `mftf.log` file. + * Overall error messages for various generation errors have been improved. Usage of the `--debug` flag provides file-specific errors for all XML-related errors. + * Allure Reports now require a unique `story` and `title` combination, to prevent collisions in Allure Report generation. + * The `features` annotation now ignores user input and defaults to the module the test lives under (for clear Allure organization). + * The `<group value="skip"/>` annotation has been replaced with a `<skip>` annotation, allowing for nested `IssueId` elements. + * Tests now require the following annotations: `stories`, `title`, `description`, `severity`. + * This will be enforced in a future major release. +* Modularity + * MFTF has been decoupled from MagentoCE: + * MFTF can now generate and run tests by itself via `bin/mftf` commands. + * It is now a top level MagentoCE dependency, and no longer relies on supporting files in MagentoCE. + * It can be used as an isolated dependency for Magento projects such as extensions. + * `generate:tests` now warns the user if any declared `<page>` has an inconsistent `module` (`Backend` vs `Magento_Backend`) + * The `--force` flag now completely ignores checking of the Magento Installation, allowing generation of tests without a Magento Instance to be running. +* Customizability + * Various test materials can now be extended via an `extends="ExistingMaterial"` attribute. This allows for creation of simple copies of any `entity`, `actionGroup`, or `test`, with small modifications. + * `test` and `actionGroup` deltas can now be provided in bulk via a `before/after` attribute on the `test` or `actionGroup` element. Deltas provided this way do not need individual `before/after` attributes, and are inserted sequentially. + * Secure and sensitive test data can now be stored and used via a new `.credentials` file, with declaration and usage syntax similar to `.env` file references. + * A new `<generateDate>` action has been added to allow users to create and use dates according to the given `date` and `format`. + * See DevDocs for more information on all above `Customizability` features. +* Maintainability + * New `bin/mftf` commands have been introduced with parity to existing `robo` commands. + * `robo` commands are still supported, but will be deprecated in a future major release. + * The `mftf upgrade:tests` command has been introduced, which runs all test upgrade scripts against the provided path. + * A new upgrade script was created to replace all test material schema paths to instead use a URN path. + * The `mftf generate:urn-catalog` command has been introduced to create a URN catalog in PHPStorm to support the above upgrade. + * A warning is now shown on generation if a page's url is referenced without specifying the url (`{{page}}` vs `{{page.url}}`). + * An error is now thrown if any test materials contain any overriding element (eg different `<element>`s in a `<section>` with the same `name`) + * This previously would cause the last read element to override the previous, causing a silent but potentially incorrect test addition. + * Test distribution algorithm for `--config parallel` has been enhanced to take average step length into account. + +### Fixes +* `_after` hook of tests now executes if a non test-related failure causes the test to error. +* Fixed periods in Allure Report showing up as `•`. +* Fixed Windows incompatibility of relative paths in various files. +* Suites will no longer generate if they do not contain any tests. +* Fixed an issue in generation where users could not use javascript variables in `executeJS` actions. +* Fixed an issue in generation where entity replacement in action-groups replaced all entities with the first reference found. +* Fixed an issue in generation where `createData` actions inside `actionGroups` could not properly reference the given `createDataKey`. +* Fixed an issue where `suites` could not generate if they included an `actionGroup` with two arguments. +* Fixed an issue in generation where calling the same entity twice (with different parameters) would replace both calls with the first resolved value. +* The `magentoCLI` action now correctly executes the given command if the `MAGENTO_BASE_URL` contains `index.php` after the domain (ex `https://magento.instance/index.php`) +* The `stepKey` attribute can no longer be an empty. +* Variable substitution has been enabled for `regex` and `command` attributes in test actions. + +### GitHub Issues/Pull requests: +* [#161](https://github.com/magento/magento2-functional-testing-framework/pull/161) -- MAGETWO-46837: Implementing extension to wait for readiness metrics. +* [#72](https://github.com/magento/magento2-functional-testing-framework/issues/72) -- declare(strict_types=1) causes static code check failure (fixed in [#154](https://github.com/magento/magento2-functional-testing-framework/pull/154)) + 2.2.0 ----- ### Enhancements From d5348a0efaa5925f11a90abe9255a20e04e61d25 Mon Sep 17 00:00:00 2001 From: KevinBKozan <kkozan@magento.com> Date: Wed, 18 Jul 2018 15:15:55 -0500 Subject: [PATCH 109/111] MQE-1147: Turning off all logging for Readiness Extension - PHPMD fixes --- .../Extension/PageReadinessExtension.php | 2 ++ .../Extension/ReadinessMetrics/AbstractMetricCheck.php | 6 ++++++ 2 files changed, 8 insertions(+) diff --git a/src/Magento/FunctionalTestingFramework/Extension/PageReadinessExtension.php b/src/Magento/FunctionalTestingFramework/Extension/PageReadinessExtension.php index 45ed651f1..9b9163a0a 100644 --- a/src/Magento/FunctionalTestingFramework/Extension/PageReadinessExtension.php +++ b/src/Magento/FunctionalTestingFramework/Extension/PageReadinessExtension.php @@ -251,6 +251,7 @@ private function shouldSkipCheck($step) * @param string $message * @param array $context * @return void + * @SuppressWarnings(PHPMD) */ private function logDebug($message, $context = []) { @@ -264,6 +265,7 @@ private function logDebug($message, $context = []) $logContext[$metric->getName() . '.failCount'] = $metric->getFailureCount(); } $context = array_merge($logContext, $context); + //TODO REMOVE THIS LINE, UNCOMMENT LOGGER //$this->logger->info($message, $context); } } diff --git a/src/Magento/FunctionalTestingFramework/Extension/ReadinessMetrics/AbstractMetricCheck.php b/src/Magento/FunctionalTestingFramework/Extension/ReadinessMetrics/AbstractMetricCheck.php index c78d2ab6f..417ae336f 100644 --- a/src/Magento/FunctionalTestingFramework/Extension/ReadinessMetrics/AbstractMetricCheck.php +++ b/src/Magento/FunctionalTestingFramework/Extension/ReadinessMetrics/AbstractMetricCheck.php @@ -306,10 +306,12 @@ public function resetTracker() * @param string $message * @param array $context * @return void + * @SuppressWarnings(PHPMD) */ protected function errorLog($message, $context = []) { $context = array_merge($this->getLogContext(), $context); + //TODO REMOVE THIS LINE, UNCOMMENT LOGGER //$this->logger->error($message, $context); } @@ -319,10 +321,12 @@ protected function errorLog($message, $context = []) * @param string $message * @param array $context * @return void + * @SuppressWarnings(PHPMD) */ protected function infoLog($message, $context = []) { $context = array_merge($this->getLogContext(), $context); + //TODO REMOVE THIS LINE, UNCOMMENT LOGGER //$this->logger->info($message, $context); } @@ -332,11 +336,13 @@ protected function infoLog($message, $context = []) * @param string $message * @param array $context * @return void + * @SuppressWarnings(PHPMD) */ protected function debugLog($message, $context = []) { if ($this->verbose) { $context = array_merge($this->getLogContext(), $context); + //TODO REMOVE THIS LINE, UNCOMMENT LOGGER //$this->logger->debug($message, $context); } } From d4076495308a4000f1b3026df5cbd7e74643e23d Mon Sep 17 00:00:00 2001 From: Kevin Kozan <kkozan@magento.com> Date: Thu, 19 Jul 2018 09:11:14 -0500 Subject: [PATCH 110/111] MQE-1147: make mftf.log visible on server - added chmod to build:project command. --- .../Console/BuildProjectCommand.php | 13 +++++++++++++ .../Util/Logger/LoggingUtil.php | 2 +- 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/src/Magento/FunctionalTestingFramework/Console/BuildProjectCommand.php b/src/Magento/FunctionalTestingFramework/Console/BuildProjectCommand.php index c5b48ba83..350a004f3 100644 --- a/src/Magento/FunctionalTestingFramework/Console/BuildProjectCommand.php +++ b/src/Magento/FunctionalTestingFramework/Console/BuildProjectCommand.php @@ -7,6 +7,7 @@ namespace Magento\FunctionalTestingFramework\Console; +use Magento\FunctionalTestingFramework\Util\Logger\LoggingUtil; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\ArrayInput; use Symfony\Component\Filesystem\Filesystem; @@ -17,6 +18,12 @@ use Magento\FunctionalTestingFramework\Util\Env\EnvProcessor; use Symfony\Component\Yaml\Yaml; +/** + * Class BuildProjectCommand + * @package Magento\FunctionalTestingFramework\Console + * + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + */ class BuildProjectCommand extends Command { const DEFAULT_YAML_INLINE_DEPTH = 10; @@ -147,6 +154,12 @@ private function generateConfigFiles(OutputInterface $output) self::CREDENTIALS_FILE_PATH ); + // Remove and Create Log File + $logPath = LoggingUtil::getInstance()->getLoggingPath(); + $fileSystem->remove($logPath); + $fileSystem->touch($logPath); + $fileSystem->chmod($logPath, 0777); + $output->writeln('.credentials.example successfully applied.'); } } diff --git a/src/Magento/FunctionalTestingFramework/Util/Logger/LoggingUtil.php b/src/Magento/FunctionalTestingFramework/Util/Logger/LoggingUtil.php index 536a4a698..78b0c29ef 100644 --- a/src/Magento/FunctionalTestingFramework/Util/Logger/LoggingUtil.php +++ b/src/Magento/FunctionalTestingFramework/Util/Logger/LoggingUtil.php @@ -75,7 +75,7 @@ public function getLogger($clazz) * * @return string */ - private function getLoggingPath() + public function getLoggingPath() { return TESTS_BP . DIRECTORY_SEPARATOR . "mftf.log"; } From e95107f82ce6bd693eee45fe271b41a6345f2b49 Mon Sep 17 00:00:00 2001 From: Kevin Kozan <kkozan@magento.com> Date: Thu, 19 Jul 2018 13:53:56 -0500 Subject: [PATCH 111/111] MQE-1114: Merge the release branch to master - Bump composer version --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 4fbea4c38..1e5dc65f8 100755 --- a/composer.json +++ b/composer.json @@ -2,7 +2,7 @@ "name": "magento/magento2-functional-testing-framework", "description": "Magento2 Functional Testing Framework", "type": "library", - "version": "2.2.0", + "version": "2.3.0", "license": "AGPL-3.0", "keywords": ["magento", "automation", "functional", "testing"], "config": {