Skip to content

Commit de1583f

Browse files
authored
MQE-1033: Validate duplicate element names in the same file (Section, Data, Metadata, Page) (magento#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="")
1 parent 6281e31 commit de1583f

File tree

15 files changed

+542
-260
lines changed

15 files changed

+542
-260
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
<?php
2+
/**
3+
* Copyright © Magento, Inc. All rights reserved.
4+
* See COPYING.txt for license details.
5+
*/
6+
7+
namespace tests\unit\Magento\FunctionalTestFramework\Test\Util;
8+
9+
use Magento\FunctionalTestingFramework\Exceptions\Collector\ExceptionCollector;
10+
use Magento\FunctionalTestingFramework\Exceptions\XmlException;
11+
use Magento\FunctionalTestingFramework\Util\Validation\DuplicateNodeValidationUtil;
12+
use Magento\FunctionalTestingFramework\Util\Validation\NameValidationUtil;
13+
use Magento\FunctionalTestingFramework\Util\MagentoTestCase;
14+
15+
class DuplicateNodeValidationUtilTest extends MagentoTestCase
16+
{
17+
/**
18+
* Validate Util flags duplicates in unique Identifiers
19+
*/
20+
public function testTestActionValidation()
21+
{
22+
// Test Data
23+
$xml = '<tests>
24+
<test name="test">
25+
<comment userInput="input1" stepKey="key1"/>
26+
<comment userInput="input2" stepKey="key1"/>
27+
</test>
28+
</tests>
29+
';
30+
$uniqueIdentifier = "stepKey";
31+
$filename = "file";
32+
33+
// Perform Test
34+
$dom = new \DOMDocument();
35+
$dom->loadXML($xml);
36+
$testNode = $dom->getElementsByTagName('test')->item(0);
37+
38+
$exceptionCollector = new ExceptionCollector();
39+
$validator = new DuplicateNodeValidationUtil($uniqueIdentifier, $exceptionCollector);
40+
$validator->validateChildUniqueness(
41+
$testNode,
42+
$filename,
43+
$uniqueIdentifier,
44+
$exceptionCollector
45+
);
46+
$this->expectException(\Exception::class);
47+
$exceptionCollector->throwException();
48+
49+
}
50+
}

etc/di.xml

+6-4
Original file line numberDiff line numberDiff line change
@@ -92,11 +92,12 @@
9292
<argument name="defaultScope" xsi:type="string">Page</argument>
9393
</arguments>
9494
</virtualType>
95-
<virtualType name="Magento\FunctionalTestingFramework\Config\Reader\Section" type="Magento\FunctionalTestingFramework\Config\Reader\Filesystem">
95+
<virtualType name="Magento\FunctionalTestingFramework\Config\Reader\Section" type="Magento\FunctionalTestingFramework\Config\Reader\MftfFilesystem">
9696
<arguments>
9797
<argument name="fileResolver" xsi:type="object">Magento\FunctionalTestingFramework\Config\FileResolver\Module</argument>
9898
<argument name="converter" xsi:type="object">Magento\FunctionalTestingFramework\Config\Converter</argument>
9999
<argument name="schemaLocator" xsi:type="object">Magento\FunctionalTestingFramework\Config\SchemaLocator\Section</argument>
100+
<argument name="domDocumentClass" xsi:type="string">Magento\FunctionalTestingFramework\Page\Config\SectionDom</argument>
100101
<argument name="idAttributes" xsi:type="array">
101102
<item name="/sections/section" xsi:type="string">name</item>
102103
<item name="/sections/section/element" xsi:type="string">name</item>
@@ -147,7 +148,7 @@
147148
<argument name="schemaPath" xsi:type="string">Magento/FunctionalTestingFramework/DataGenerator/etc/dataProfileSchema.xsd</argument>
148149
</arguments>
149150
</virtualType>
150-
<virtualType name="Magento\FunctionalTestingFramework\Config\Reader\DataProfile" type="Magento\FunctionalTestingFramework\DataGenerator\Config\Reader\Filesystem">
151+
<virtualType name="Magento\FunctionalTestingFramework\Config\Reader\DataProfile" type="Magento\FunctionalTestingFramework\Config\Reader\MftfFilesystem">
151152
<arguments>
152153
<argument name="fileResolver" xsi:type="object">Magento\FunctionalTestingFramework\Config\FileResolver\Module</argument>
153154
<argument name="converter" xsi:type="object">Magento\FunctionalTestingFramework\Config\Converter</argument>
@@ -156,6 +157,7 @@
156157
<argument name="idAttributes" xsi:type="array">
157158
<item name="/entities/entity" xsi:type="string">name</item>
158159
<item name="/entities/entity/(data|array)" xsi:type="string">key</item>
160+
<item name="/entities/entity/requiredEntity" xsi:type="string">type</item>
159161
</argument>
160162
<argument name="mergeablePaths" xsi:type="array">
161163
<item name="/entities/entity/requiredEntity" xsi:type="string"/>
@@ -183,11 +185,11 @@
183185
<argument name="schemaPath" xsi:type="string">Magento/FunctionalTestingFramework/DataGenerator/etc/dataOperation.xsd</argument>
184186
</arguments>
185187
</virtualType>
186-
<virtualType name="Magento\FunctionalTestingFramework\Config\Reader\Metadata" type="Magento\FunctionalTestingFramework\DataGenerator\Config\Reader\Filesystem">
188+
<virtualType name="Magento\FunctionalTestingFramework\Config\Reader\Metadata" type="Magento\FunctionalTestingFramework\Config\Reader\MftfFilesystem">
187189
<arguments>
188190
<argument name="fileResolver" xsi:type="object">Magento\FunctionalTestingFramework\Config\FileResolver\Module</argument>
189191
<argument name="converter" xsi:type="object">Magento\FunctionalTestingFramework\Config\Converter</argument>
190-
<argument name="domDocumentClass" xsi:type="string">Magento\FunctionalTestingFramework\DataGenerator\Config\Dom</argument>
192+
<argument name="domDocumentClass" xsi:type="string">Magento\FunctionalTestingFramework\DataGenerator\Config\OperationDom</argument>
191193
<argument name="schemaLocator" xsi:type="object">Magento\FunctionalTestingFramework\Config\SchemaLocator\Metadata</argument>
192194
<argument name="idAttributes" xsi:type="array">
193195
<item name="/operations/operation" xsi:type="string">name</item>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
<?php
2+
/**
3+
* Copyright © Magento, Inc. All rights reserved.
4+
* See COPYING.txt for license details.
5+
*/
6+
7+
namespace Magento\FunctionalTestingFramework\Config;
8+
9+
use Magento\FunctionalTestingFramework\Exceptions\Collector\ExceptionCollector;
10+
use Magento\FunctionalTestingFramework\Exceptions\XmlException;
11+
use Magento\FunctionalTestingFramework\Config\Dom\NodeMergingConfig;
12+
use Magento\FunctionalTestingFramework\Config\Dom\NodePathMatcher;
13+
use Magento\FunctionalTestingFramework\Test\Objects\ActionObject;
14+
use Magento\FunctionalTestingFramework\Util\Validation\DuplicateNodeValidationUtil;
15+
16+
/**
17+
* Class MftfDom
18+
* @package Magento\FunctionalTestingFramework\Config
19+
*/
20+
class MftfDom extends \Magento\FunctionalTestingFramework\Config\Dom
21+
{
22+
/**
23+
* MftfDom constructor.
24+
* @param string $xml
25+
* @param string $filename
26+
* @param ExceptionCollector $exceptionCollector
27+
* @param array $idAttributes
28+
* @param string $typeAttributeName
29+
* @param string $schemaFile
30+
* @param string $errorFormat
31+
*/
32+
public function __construct(
33+
$xml,
34+
$filename,
35+
$exceptionCollector,
36+
array $idAttributes = [],
37+
$typeAttributeName = null,
38+
$schemaFile = null,
39+
$errorFormat = self::ERROR_FORMAT_DEFAULT
40+
) {
41+
$this->schemaFile = $schemaFile;
42+
$this->nodeMergingConfig = new NodeMergingConfig(new NodePathMatcher(), $idAttributes);
43+
$this->typeAttributeName = $typeAttributeName;
44+
$this->errorFormat = $errorFormat;
45+
$this->dom = $this->initDom($xml, $filename, $exceptionCollector);
46+
$this->rootNamespace = $this->dom->lookupNamespaceUri($this->dom->namespaceURI);
47+
}
48+
49+
/**
50+
* Redirects any merges into the init method for appending xml filename
51+
*
52+
* @param string $xml
53+
* @param string|null $filename
54+
* @param ExceptionCollector $exceptionCollector
55+
* @return void
56+
*/
57+
public function merge($xml, $filename = null, $exceptionCollector = null)
58+
{
59+
$dom = $this->initDom($xml, $filename, $exceptionCollector);
60+
$this->mergeNode($dom->documentElement, '');
61+
}
62+
}

src/Magento/FunctionalTestingFramework/DataGenerator/Config/Dom.php

+42-81
Original file line numberDiff line numberDiff line change
@@ -7,117 +7,78 @@
77

88
use Magento\FunctionalTestingFramework\Config\Dom\NodeMergingConfig;
99
use Magento\FunctionalTestingFramework\Config\Dom\NodePathMatcher;
10+
use Magento\FunctionalTestingFramework\Exceptions\Collector\ExceptionCollector;
11+
use Magento\FunctionalTestingFramework\Util\Validation\DuplicateNodeValidationUtil;
1012

1113
/**
12-
* Magento configuration XML DOM utility
14+
* MFTF actionGroup.xml configuration XML DOM utility
15+
* @package Magento\FunctionalTestingFramework\DataGenerator\Config
1316
*/
14-
class Dom extends \Magento\FunctionalTestingFramework\Config\Dom
17+
class Dom extends \Magento\FunctionalTestingFramework\Config\MftfDom
1518
{
19+
const DATA_FILE_NAME_ENDING = "Data";
20+
const DATA_META_FILENAME_ATTRIBUTE = "filename";
1621

1722
/**
18-
* Array of non keyed mergeable paths
19-
*
20-
* @var array
23+
* NodeValidationUtil
24+
* @var DuplicateNodeValidationUtil
2125
*/
22-
private $mergeablePaths;
26+
private $validationUtil;
2327

2428
/**
25-
* Build DOM with initial XML contents and specifying identifier attributes for merging. Overridden to include new
26-
* mergeablePaths argument which can be matched for non keyed mergeable xml elements.
27-
*
28-
* Format of $idAttributes: array('/xpath/to/some/node' => 'id_attribute_name')
29-
* The path to ID attribute name should not include any attribute notations or modifiers -- only node names
30-
*
29+
* Entity Dom constructor.
3130
* @param string $xml
31+
* @param string $filename
32+
* @param ExceptionCollector $exceptionCollector
3233
* @param array $idAttributes
33-
* @param array $mergeablePaths
3434
* @param string $typeAttributeName
3535
* @param string $schemaFile
3636
* @param string $errorFormat
3737
*/
3838
public function __construct(
3939
$xml,
40+
$filename,
41+
$exceptionCollector,
4042
array $idAttributes = [],
41-
array $mergeablePaths = [],
4243
$typeAttributeName = null,
4344
$schemaFile = null,
4445
$errorFormat = self::ERROR_FORMAT_DEFAULT
4546
) {
46-
$this->schemaFile = $schemaFile;
47-
$this->nodeMergingConfig = new NodeMergingConfig(new NodePathMatcher(), $idAttributes);
48-
$this->mergeablePaths = $mergeablePaths;
49-
$this->typeAttributeName = $typeAttributeName;
50-
$this->errorFormat = $errorFormat;
51-
$this->dom = $this->initDom($xml);
52-
$this->rootNamespace = $this->dom->lookupNamespaceUri($this->dom->namespaceURI);
53-
}
54-
55-
/**
56-
* Recursive merging of the \DOMElement into the original document. Overridden to include a call to
57-
*
58-
* Algorithm:
59-
* 1. Find the same node in original document
60-
* 2. Extend and override original document node attributes and scalar value if found
61-
* 3. Append new node if original document doesn't have the same node
62-
*
63-
* @param \DOMElement $node
64-
* @param string $parentPath path to parent node
65-
* @return void
66-
*/
67-
public function mergeNode(\DOMElement $node, $parentPath)
68-
{
69-
$path = $this->getNodePathByParent($node, $parentPath);
70-
$isMergeablePath = $this->validateIsPathMergeable($path);
71-
72-
$matchedNode = $this->getMatchedNode($path, $isMergeablePath);
73-
74-
/* Update matched node attributes and value */
75-
if ($matchedNode && !$isMergeablePath) {
76-
//different node type
77-
$this->mergeMatchingNode($node, $parentPath, $matchedNode, $path);
78-
} else {
79-
/* Add node as is to the document under the same parent element */
80-
$parentMatchedNode = $this->getMatchedNode($parentPath);
81-
$newNode = $this->dom->importNode($node, true);
82-
$parentMatchedNode->appendChild($newNode);
83-
}
47+
$this->validationUtil = new DuplicateNodeValidationUtil('key', $exceptionCollector);
48+
parent::__construct(
49+
$xml,
50+
$filename,
51+
$exceptionCollector,
52+
$idAttributes,
53+
$typeAttributeName,
54+
$schemaFile,
55+
$errorFormat
56+
);
8457
}
8558

8659
/**
87-
* Getter for node by path, overridden to include validation flag for mergeable entries
88-
* An exception is possible if original document contains multiple nodes for identifier
60+
* Takes a dom element from xml and appends the filename based on location
8961
*
90-
* @param string $nodePath
91-
* @param boolean $isMergeablePath
92-
* @throws \Exception
93-
* @return \DOMElement|null
62+
* @param string $xml
63+
* @param string|null $filename
64+
* @return \DOMDocument
9465
*/
95-
public function getMatchedNode($nodePath, $isMergeablePath = false)
66+
public function initDom($xml, $filename = null)
9667
{
97-
$xPath = new \DOMXPath($this->dom);
98-
if ($this->rootNamespace) {
99-
$xPath->registerNamespace(self::ROOT_NAMESPACE_PREFIX, $this->rootNamespace);
100-
}
101-
$matchedNodes = $xPath->query($nodePath);
102-
$node = null;
68+
$dom = parent::initDom($xml);
10369

104-
if ($matchedNodes->length > 1 && !$isMergeablePath) {
105-
throw new \Exception("More than one node matching the query: {$nodePath}");
106-
} elseif ($matchedNodes->length == 1) {
107-
$node = $matchedNodes->item(0);
70+
if (strpos($filename, self::DATA_FILE_NAME_ENDING)) {
71+
$entityNodes = $dom->getElementsByTagName('entity');
72+
foreach ($entityNodes as $entityNode) {
73+
/** @var \DOMElement $entityNode */
74+
$entityNode->setAttribute(self::DATA_META_FILENAME_ATTRIBUTE, $filename);
75+
$this->validationUtil->validateChildUniqueness(
76+
$entityNode,
77+
$filename
78+
);
79+
}
10880
}
109-
return $node;
110-
}
11181

112-
/**
113-
* Function which simplifies and xpath match in dom and compares with listed known mergeable paths
114-
*
115-
* @param string $path
116-
* @return boolean
117-
*/
118-
private function validateIsPathMergeable($path)
119-
{
120-
$simplifiedPath = $this->nodeMergingConfig->getNodePathMatcher()->simplifyXpath($path);
121-
return array_key_exists($simplifiedPath, $this->mergeablePaths);
82+
return $dom;
12283
}
12384
}

0 commit comments

Comments
 (0)