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..b7ea66f65 --- /dev/null +++ b/dev/tests/unit/Magento/FunctionalTestFramework/Test/Config/ActionGroupDomTest.php @@ -0,0 +1,32 @@ + + + + + + "; + + $exceptionCollector = new ExceptionCollector(); + $actionDom = new ActionGroupDom($sampleXml, 'dupeStepKeyActionGroup.xml', $exceptionCollector); + + $this->expectException(\Exception::class); + $exceptionCollector->throwException(); + } +} 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/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 new file mode 100644 index 000000000..794007dde --- /dev/null +++ b/src/Magento/FunctionalTestingFramework/Test/Config/ActionGroupDom.php @@ -0,0 +1,38 @@ +getElementsByTagName('actionGroup'); + foreach ($actionGroupNodes as $actionGroupNode) { + /** @var \DOMElement $actionGroupNode */ + $actionGroupNode->setAttribute(self::TEST_META_FILENAME_ATTRIBUTE, $filename); + $this->validateDomStepKeys($actionGroupNode, $filename, 'Action Group', $exceptionCollector); + } + } + + return $dom; + } +} diff --git a/src/Magento/FunctionalTestingFramework/Test/Config/Dom.php b/src/Magento/FunctionalTestingFramework/Test/Config/Dom.php index a2d9c180f..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->validateTestDomStepKeys($testNode, $filename); + $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, ''); } @@ -83,10 +88,12 @@ public function merge($xml, $filename = null) * * @param \DOMElement $testNode * @param string $filename + * @param string $type + * @param ExceptionCollector $exceptionCollector * @return void * @throws XmlException */ - private function validateTestDomStepKeys($testNode, $filename) + protected function validateDomStepKeys($testNode, $filename, $type, $exceptionCollector) { $childNodes = $testNode->childNodes; @@ -99,7 +106,7 @@ private function validateTestDomStepKeys($testNode, $filename) } if (in_array($currentNode->nodeName, self::TEST_HOOK_NAMES)) { - $this->validateTestDomStepKeys($currentNode, $filename); + $this->validateDomStepKeys($currentNode, $filename, $type, $exceptionCollector); } if ($currentNode->hasAttribute('stepKey')) { @@ -116,7 +123,8 @@ 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}"); + $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 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 @@ +