From a3bb056e9e7e999c840412de6f925a616b051d47 Mon Sep 17 00:00:00 2001 From: Ian Meron Date: Wed, 11 Apr 2018 11:41:05 -0500 Subject: [PATCH 1/2] MQE-790: Error for duplicate step keys in a single action group definition - add new ActionGroup DOM for stepKey validation - update ActionGroup di.xml config to use new DOM - add unit test -fix verification test --- .../Test/Config/ActionGroupDomTest.php | 32 +++++++++++++++++ .../ActionGroup/XmlDuplicateActionGroup.xml | 8 ++--- etc/di.xml | 3 +- .../Test/Config/ActionGroupDom.php | 35 +++++++++++++++++++ .../Test/Config/Dom.php | 11 +++--- .../Test/Util/ActionGroupObjectExtractor.php | 5 ++- .../Test/etc/actionGroupSchema.xsd | 1 + 7 files changed, 85 insertions(+), 10 deletions(-) create mode 100644 dev/tests/unit/Magento/FunctionalTestFramework/Test/Config/ActionGroupDomTest.php create mode 100644 src/Magento/FunctionalTestingFramework/Test/Config/ActionGroupDom.php diff --git a/dev/tests/unit/Magento/FunctionalTestFramework/Test/Config/ActionGroupDomTest.php b/dev/tests/unit/Magento/FunctionalTestFramework/Test/Config/ActionGroupDomTest.php new file mode 100644 index 000000000..50b23c045 --- /dev/null +++ b/dev/tests/unit/Magento/FunctionalTestFramework/Test/Config/ActionGroupDomTest.php @@ -0,0 +1,32 @@ + + + + + + "; + + $actionDom = new ActionGroupDom($sampleXml, 'test.xml'); + $this->expectException(XmlException::class); + + // an exception is only thrown for Action Group files. + $actionDom->initDom($sampleXml, 'dupeStepKeyActionGroup.xml'); + } +} diff --git a/dev/tests/verification/TestModule/ActionGroup/XmlDuplicateActionGroup.xml b/dev/tests/verification/TestModule/ActionGroup/XmlDuplicateActionGroup.xml index 8c5ba7402..fcc22acca 100644 --- a/dev/tests/verification/TestModule/ActionGroup/XmlDuplicateActionGroup.xml +++ b/dev/tests/verification/TestModule/ActionGroup/XmlDuplicateActionGroup.xml @@ -16,8 +16,8 @@ - - + + @@ -164,8 +164,8 @@ - - + + diff --git a/etc/di.xml b/etc/di.xml index e52c58b31..d03ed2267 100644 --- a/etc/di.xml +++ b/etc/di.xml @@ -286,11 +286,12 @@ - + Magento\FunctionalTestingFramework\Config\FileResolver\Module Magento\FunctionalTestingFramework\Config\ActionGroupDataConverter Magento\FunctionalTestingFramework\Config\SchemaLocator\ActionGroup + Magento\FunctionalTestingFramework\Test\Config\ActionGroupDom name name diff --git a/src/Magento/FunctionalTestingFramework/Test/Config/ActionGroupDom.php b/src/Magento/FunctionalTestingFramework/Test/Config/ActionGroupDom.php new file mode 100644 index 000000000..9bd874a57 --- /dev/null +++ b/src/Magento/FunctionalTestingFramework/Test/Config/ActionGroupDom.php @@ -0,0 +1,35 @@ +getElementsByTagName('actionGroup'); + foreach ($actionGroupNodes as $actionGroupNode) { + /** @var \DOMElement $actionGroupNode */ + $actionGroupNode->setAttribute(self::TEST_META_FILENAME_ATTRIBUTE, $filename); + $this->validateDomStepKeys($actionGroupNode, $filename, 'Action Group'); + } + } + + return $dom; + } +} diff --git a/src/Magento/FunctionalTestingFramework/Test/Config/Dom.php b/src/Magento/FunctionalTestingFramework/Test/Config/Dom.php index a2d9c180f..44b990b3f 100644 --- a/src/Magento/FunctionalTestingFramework/Test/Config/Dom.php +++ b/src/Magento/FunctionalTestingFramework/Test/Config/Dom.php @@ -58,7 +58,7 @@ public function initDom($xml, $filename = null) foreach ($testNodes as $testNode) { /** @var \DOMElement $testNode */ $testNode->setAttribute(self::TEST_META_FILENAME_ATTRIBUTE, $filename); - $this->validateTestDomStepKeys($testNode, $filename); + $this->validateDomStepKeys($testNode, $filename, 'Test'); } } @@ -83,10 +83,11 @@ public function merge($xml, $filename = null) * * @param \DOMElement $testNode * @param string $filename + * @param string $type * @return void * @throws XmlException */ - private function validateTestDomStepKeys($testNode, $filename) + protected function validateDomStepKeys($testNode, $filename, $type) { $childNodes = $testNode->childNodes; @@ -99,7 +100,7 @@ private function validateTestDomStepKeys($testNode, $filename) } if (in_array($currentNode->nodeName, self::TEST_HOOK_NAMES)) { - $this->validateTestDomStepKeys($currentNode, $filename); + $this->validateDomStepKeys($currentNode, $filename, $type); } if ($currentNode->hasAttribute('stepKey')) { @@ -116,7 +117,9 @@ private function validateTestDomStepKeys($testNode, $filename) $stepKeyError .= "\tstepKey: {$duplicateValue} is used more than once.\n"; } - throw new XmlException("Tests cannot use stepKey more than once!\t\n{$stepKeyError}\tin file: {$filename}"); + throw new XmlException( + "{$type}s cannot use stepKey more than once!\t\n{$stepKeyError}\tin file: {$filename}" + ); } } } diff --git a/src/Magento/FunctionalTestingFramework/Test/Util/ActionGroupObjectExtractor.php b/src/Magento/FunctionalTestingFramework/Test/Util/ActionGroupObjectExtractor.php index a49246d44..aeef21760 100644 --- a/src/Magento/FunctionalTestingFramework/Test/Util/ActionGroupObjectExtractor.php +++ b/src/Magento/FunctionalTestingFramework/Test/Util/ActionGroupObjectExtractor.php @@ -17,6 +17,7 @@ class ActionGroupObjectExtractor extends BaseObjectExtractor { const DEFAULT_VALUE = 'defaultValue'; const ACTION_GROUP_ARGUMENTS = 'arguments'; + const FILENAME = 'filename'; /** * Action Object Extractor for converting actions into objects @@ -47,9 +48,11 @@ public function extractActionGroup($actionGroupData) $actionGroupData, self::NODE_NAME, self::ACTION_GROUP_ARGUMENTS, - self::NAME + self::NAME, + self::FILENAME ); + // TODO filename is now available to the ActionGroupObject, integrate this into debug and error statements $actions = $this->actionObjectExtractor->extractActions($actionData); if (array_key_exists(self::ACTION_GROUP_ARGUMENTS, $actionGroupData)) { diff --git a/src/Magento/FunctionalTestingFramework/Test/etc/actionGroupSchema.xsd b/src/Magento/FunctionalTestingFramework/Test/etc/actionGroupSchema.xsd index 5c5fc0059..74badd40c 100644 --- a/src/Magento/FunctionalTestingFramework/Test/etc/actionGroupSchema.xsd +++ b/src/Magento/FunctionalTestingFramework/Test/etc/actionGroupSchema.xsd @@ -32,6 +32,7 @@ + From ab81af625f89b0d2fdff9778960da3005d6fb914 Mon Sep 17 00:00:00 2001 From: Ian Meron Date: Thu, 12 Apr 2018 14:00:43 -0500 Subject: [PATCH 2/2] MQE-790: Error for duplicate step keys in a single action group definition - add new ExceptionCollector to throw all exceptions following stepKey validation --- .../Test/Config/ActionGroupDomTest.php | 10 +-- .../Collector/ExceptionCollector.php | 67 +++++++++++++++++++ .../Test/Config/ActionGroupDom.php | 7 +- .../Test/Config/Dom.php | 25 ++++--- .../Test/Config/Reader/Filesystem.php | 14 ++-- 5 files changed, 102 insertions(+), 21 deletions(-) create mode 100644 src/Magento/FunctionalTestingFramework/Exceptions/Collector/ExceptionCollector.php diff --git a/dev/tests/unit/Magento/FunctionalTestFramework/Test/Config/ActionGroupDomTest.php b/dev/tests/unit/Magento/FunctionalTestFramework/Test/Config/ActionGroupDomTest.php index 50b23c045..b7ea66f65 100644 --- a/dev/tests/unit/Magento/FunctionalTestFramework/Test/Config/ActionGroupDomTest.php +++ b/dev/tests/unit/Magento/FunctionalTestFramework/Test/Config/ActionGroupDomTest.php @@ -5,7 +5,7 @@ */ namespace Tests\unit\Magento\FunctionalTestFramework\Test\Config; -use Magento\FunctionalTestingFramework\Exceptions\XmlException; +use Magento\FunctionalTestingFramework\Exceptions\Collector\ExceptionCollector; use Magento\FunctionalTestingFramework\Test\Config\ActionGroupDom; use PHPUnit\Framework\TestCase; @@ -23,10 +23,10 @@ public function testActionGroupDomStepKeyValidation() "; - $actionDom = new ActionGroupDom($sampleXml, 'test.xml'); - $this->expectException(XmlException::class); + $exceptionCollector = new ExceptionCollector(); + $actionDom = new ActionGroupDom($sampleXml, 'dupeStepKeyActionGroup.xml', $exceptionCollector); - // an exception is only thrown for Action Group files. - $actionDom->initDom($sampleXml, 'dupeStepKeyActionGroup.xml'); + $this->expectException(\Exception::class); + $exceptionCollector->throwException(); } } diff --git a/src/Magento/FunctionalTestingFramework/Exceptions/Collector/ExceptionCollector.php b/src/Magento/FunctionalTestingFramework/Exceptions/Collector/ExceptionCollector.php new file mode 100644 index 000000000..4b67add0b --- /dev/null +++ b/src/Magento/FunctionalTestingFramework/Exceptions/Collector/ExceptionCollector.php @@ -0,0 +1,67 @@ +errors = array_merge_recursive($this->errors, $error); + } + + /** + * Function which throws an exception when there are errors present. + * + * @return void + * @throws \Exception + */ + public function throwException() + { + if (empty($this->errors)) { + return; + } + + $errorMsg = implode("\n\n", $this->formatErrors($this->errors)); + throw new \Exception("\n" . $errorMsg); + } + + /** + * If there are multiple exceptions for a single file, the function flattens the array so they can be printed + * as separate messages. + * + * @param array $errors + * @return array + */ + private function formatErrors($errors) + { + $flattenedErrors = []; + foreach ($errors as $key => $errorMsg) { + if (is_array($errorMsg)) { + $flattenedErrors = array_merge($flattenedErrors, $this->formatErrors($errorMsg)); + continue; + } + + $flattenedErrors[] = $errorMsg; + } + + return $flattenedErrors; + } +} diff --git a/src/Magento/FunctionalTestingFramework/Test/Config/ActionGroupDom.php b/src/Magento/FunctionalTestingFramework/Test/Config/ActionGroupDom.php index 9bd874a57..794007dde 100644 --- a/src/Magento/FunctionalTestingFramework/Test/Config/ActionGroupDom.php +++ b/src/Magento/FunctionalTestingFramework/Test/Config/ActionGroupDom.php @@ -5,6 +5,8 @@ */ namespace Magento\FunctionalTestingFramework\Test\Config; +use Magento\FunctionalTestingFramework\Exceptions\Collector\ExceptionCollector; + class ActionGroupDom extends Dom { const ACTION_GROUP_FILE_NAME_ENDING = "ActionGroup.xml"; @@ -15,9 +17,10 @@ class ActionGroupDom extends Dom * * @param string $xml * @param string|null $filename + * @param ExceptionCollector $exceptionCollector * @return \DOMDocument */ - public function initDom($xml, $filename = null) + public function initDom($xml, $filename = null, $exceptionCollector = null) { $dom = parent::initDom($xml); @@ -26,7 +29,7 @@ public function initDom($xml, $filename = null) foreach ($actionGroupNodes as $actionGroupNode) { /** @var \DOMElement $actionGroupNode */ $actionGroupNode->setAttribute(self::TEST_META_FILENAME_ATTRIBUTE, $filename); - $this->validateDomStepKeys($actionGroupNode, $filename, 'Action Group'); + $this->validateDomStepKeys($actionGroupNode, $filename, 'Action Group', $exceptionCollector); } } diff --git a/src/Magento/FunctionalTestingFramework/Test/Config/Dom.php b/src/Magento/FunctionalTestingFramework/Test/Config/Dom.php index 44b990b3f..24710243f 100644 --- a/src/Magento/FunctionalTestingFramework/Test/Config/Dom.php +++ b/src/Magento/FunctionalTestingFramework/Test/Config/Dom.php @@ -6,6 +6,7 @@ namespace Magento\FunctionalTestingFramework\Test\Config; +use Magento\FunctionalTestingFramework\Exceptions\Collector\ExceptionCollector; use Magento\FunctionalTestingFramework\Exceptions\XmlException; use Magento\FunctionalTestingFramework\Config\Dom\NodeMergingConfig; use Magento\FunctionalTestingFramework\Config\Dom\NodePathMatcher; @@ -21,6 +22,7 @@ class Dom extends \Magento\FunctionalTestingFramework\Config\Dom * TestDom constructor. * @param string $xml * @param string $filename + * @param ExceptionCollector $exceptionCollector * @param array $idAttributes * @param string $typeAttributeName * @param string $schemaFile @@ -29,6 +31,7 @@ class Dom extends \Magento\FunctionalTestingFramework\Config\Dom public function __construct( $xml, $filename, + $exceptionCollector, array $idAttributes = [], $typeAttributeName = null, $schemaFile = null, @@ -38,7 +41,7 @@ public function __construct( $this->nodeMergingConfig = new NodeMergingConfig(new NodePathMatcher(), $idAttributes); $this->typeAttributeName = $typeAttributeName; $this->errorFormat = $errorFormat; - $this->dom = $this->initDom($xml, $filename); + $this->dom = $this->initDom($xml, $filename, $exceptionCollector); $this->rootNamespace = $this->dom->lookupNamespaceUri($this->dom->namespaceURI); } @@ -47,9 +50,10 @@ public function __construct( * * @param string $xml * @param string|null $filename + * @param ExceptionCollector $exceptionCollector * @return \DOMDocument */ - public function initDom($xml, $filename = null) + public function initDom($xml, $filename = null, $exceptionCollector = null) { $dom = parent::initDom($xml); @@ -58,7 +62,7 @@ public function initDom($xml, $filename = null) foreach ($testNodes as $testNode) { /** @var \DOMElement $testNode */ $testNode->setAttribute(self::TEST_META_FILENAME_ATTRIBUTE, $filename); - $this->validateDomStepKeys($testNode, $filename, 'Test'); + $this->validateDomStepKeys($testNode, $filename, 'Test', $exceptionCollector); } } @@ -70,11 +74,12 @@ public function initDom($xml, $filename = null) * * @param string $xml * @param string|null $filename + * @param ExceptionCollector $exceptionCollector * @return void */ - public function merge($xml, $filename = null) + public function merge($xml, $filename = null, $exceptionCollector = null) { - $dom = $this->initDom($xml, $filename); + $dom = $this->initDom($xml, $filename, $exceptionCollector); $this->mergeNode($dom->documentElement, ''); } @@ -84,10 +89,11 @@ public function merge($xml, $filename = null) * @param \DOMElement $testNode * @param string $filename * @param string $type + * @param ExceptionCollector $exceptionCollector * @return void * @throws XmlException */ - protected function validateDomStepKeys($testNode, $filename, $type) + protected function validateDomStepKeys($testNode, $filename, $type, $exceptionCollector) { $childNodes = $testNode->childNodes; @@ -100,7 +106,7 @@ protected function validateDomStepKeys($testNode, $filename, $type) } if (in_array($currentNode->nodeName, self::TEST_HOOK_NAMES)) { - $this->validateDomStepKeys($currentNode, $filename, $type); + $this->validateDomStepKeys($currentNode, $filename, $type, $exceptionCollector); } if ($currentNode->hasAttribute('stepKey')) { @@ -117,9 +123,8 @@ protected function validateDomStepKeys($testNode, $filename, $type) $stepKeyError .= "\tstepKey: {$duplicateValue} is used more than once.\n"; } - throw new XmlException( - "{$type}s cannot use stepKey more than once!\t\n{$stepKeyError}\tin file: {$filename}" - ); + $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/Test/Config/Reader/Filesystem.php b/src/Magento/FunctionalTestingFramework/Test/Config/Reader/Filesystem.php index c2d16a13a..d96a926c5 100644 --- a/src/Magento/FunctionalTestingFramework/Test/Config/Reader/Filesystem.php +++ b/src/Magento/FunctionalTestingFramework/Test/Config/Reader/Filesystem.php @@ -6,6 +6,7 @@ namespace Magento\FunctionalTestingFramework\Test\Config\Reader; +use Magento\FunctionalTestingFramework\Exceptions\Collector\ExceptionCollector; use Magento\FunctionalTestingFramework\Util\Iterator\File; class Filesystem extends \Magento\FunctionalTestingFramework\Config\Reader\Filesystem @@ -19,6 +20,8 @@ class Filesystem extends \Magento\FunctionalTestingFramework\Config\Reader\Files */ public function readFiles($fileList) { + $exceptionCollector = new ExceptionCollector(); + $errors = []; /** @var \Magento\FunctionalTestingFramework\Test\Config\Dom $configMerger */ $configMerger = null; foreach ($fileList as $key => $content) { @@ -27,17 +30,18 @@ public function readFiles($fileList) $configMerger = $this->createConfigMerger( $this->domDocumentClass, $content, - $fileList->getFilename() + $fileList->getFilename(), + $exceptionCollector ); } else { - $configMerger->merge($content, $fileList->getFilename()); + $configMerger->merge($content, $fileList->getFilename(), $exceptionCollector); } } catch (\Magento\FunctionalTestingFramework\Config\Dom\ValidationException $e) { throw new \Exception("Invalid XML in file " . $key . ":\n" . $e->getMessage()); } } + $exceptionCollector->throwException(); if ($this->validationState->isValidationRequired()) { - $errors = []; if ($configMerger && !$configMerger->validate($this->schemaFile, $errors)) { $message = "Invalid Document \n"; throw new \Exception($message . implode("\n", $errors)); @@ -57,14 +61,16 @@ public function readFiles($fileList) * @param string $mergerClass * @param string $initialContents * @param string $filename + * @param ExceptionCollector $exceptionCollector * @return \Magento\FunctionalTestingFramework\Config\Dom * @throws \UnexpectedValueException */ - protected function createConfigMerger($mergerClass, $initialContents, $filename = null) + protected function createConfigMerger($mergerClass, $initialContents, $filename = null, $exceptionCollector = null) { $result = new $mergerClass( $initialContents, $filename, + $exceptionCollector, $this->idAttributes, null, $this->perFileSchema