Skip to content

Commit b18fbc2

Browse files
committed
MQE-345:[DevOps] [Customizability] Create suite schema declaration and supporting interpretation
- add new suite objects, handler, parser classes - alter di.xml file to include new suite parsing - add new resolver for root level dir - change cest objects and test generators to fit with suite design
1 parent a0f53f8 commit b18fbc2

File tree

17 files changed

+842
-58
lines changed

17 files changed

+842
-58
lines changed

etc/di.xml

+32
Original file line numberDiff line numberDiff line change
@@ -309,4 +309,36 @@
309309
<argument name="reader" xsi:type="object">Magento\FunctionalTestingFramework\Config\Reader\ActionGroupData</argument>
310310
</arguments>
311311
</virtualType>
312+
313+
<!--Config for Suite Data -->
314+
315+
<type name="Magento\FunctionalTestingFramework\Suite\Parsers\SuiteDataParser">
316+
<arguments>
317+
<argument name="suiteData" xsi:type="object">Magento\FunctionalTestingFramework\Suite\Config\SuiteData</argument>
318+
</arguments>
319+
</type>
320+
<virtualType name="Magento\FunctionalTestingFramework\Suite\Config\SuiteData" type="Magento\FunctionalTestingFramework\Config\Data">
321+
<arguments>
322+
<argument name="reader" xsi:type="object">Magento\FunctionalTestingFramework\Config\Reader\SuiteData</argument>
323+
</arguments>
324+
</virtualType>
325+
<virtualType name="Magento\FunctionalTestingFramework\Config\SchemaLocator\SuiteData" type="Magento\FunctionalTestingFramework\Config\SchemaLocator">
326+
<arguments>
327+
<argument name="schemaPath" xsi:type="string">Magento/FunctionalTestingFramework/Suite/etc/suiteSchema.xsd</argument>
328+
</arguments>
329+
</virtualType>
330+
<virtualType name="Magento\FunctionalTestingFramework\Config\Reader\SuiteData" type="Magento\FunctionalTestingFramework\Config\Reader\Filesystem">
331+
<arguments>
332+
<argument name="fileResolver" xsi:type="object">Magento\FunctionalTestingFramework\Config\FileResolver\Root</argument>
333+
<argument name="converter" xsi:type="object">Magento\FunctionalTestingFramework\Config\Converter</argument>
334+
<argument name="schemaLocator" xsi:type="object">Magento\FunctionalTestingFramework\Config\SchemaLocator\SuiteData</argument>
335+
<argument name="idAttributes" xsi:type="array">
336+
<item name="/config/suite" xsi:type="string">name</item>
337+
<item name="/config/suite/include/group" xsi:type="string">name</item>
338+
<item name="/config/suite/exclude/group" xsi:type="string">name</item>
339+
</argument>
340+
<argument name="fileName" xsi:type="string">*.xml</argument>
341+
<argument name="defaultScope" xsi:type="string">_suite</argument>
342+
</arguments>
343+
</virtualType>
312344
</config>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
<?php
2+
/**
3+
* Copyright © 2017 Magento. All rights reserved.
4+
* See COPYING.txt for license details.
5+
*/
6+
namespace Magento\FunctionalTestingFramework\Config\FileResolver;
7+
8+
use Magento\FunctionalTestingFramework\Config\FileResolverInterface;
9+
use Magento\FunctionalTestingFramework\Util\Iterator\File;
10+
11+
class Root implements FileResolverInterface
12+
{
13+
14+
/**
15+
* Retrieve the list of configuration files with given name that relate to specified scope at the tests level
16+
*
17+
* @param string $filename
18+
* @param string $scope
19+
* @return array|\Iterator,\Countable
20+
*/
21+
public function get($filename, $scope)
22+
{
23+
$paths = glob(dirname(TESTS_BP) . DIRECTORY_SEPARATOR . $scope . DIRECTORY_SEPARATOR . $filename);
24+
25+
return new File($paths);
26+
}
27+
}

src/Magento/FunctionalTestingFramework/DataGenerator/Objects/EntityDataObject.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -204,7 +204,7 @@ public function getDataByName($dataName, $uniDataFormat)
204204
* category->id)
205205
*
206206
* @param string $dataKey
207-
* @return array|null
207+
* @return string|null
208208
*/
209209
public function getVarReference($dataKey)
210210
{

src/Magento/FunctionalTestingFramework/ObjectManager/ObjectHandlerInterface.php

+2-2
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,10 @@ public static function getInstance();
2121
/**
2222
* Function to return a single object by name
2323
*
24-
* @param string $jsonDefitionName
24+
* @param string $objectName
2525
* @return object
2626
*/
27-
public function getObject($jsonDefitionName);
27+
public function getObject($objectName);
2828

2929
/**
3030
* Function to return all objects the handler is responsible for
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,248 @@
1+
<?php
2+
/**
3+
* Copyright © Magento, Inc. All rights reserved.
4+
* See COPYING.txt for license details.
5+
*/
6+
namespace Magento\FunctionalTestingFramework\Suite\Handlers;
7+
8+
use Exception;
9+
use Magento\FunctionalTestingFramework\ObjectManager\ObjectHandlerInterface;
10+
use Magento\FunctionalTestingFramework\ObjectManagerFactory;
11+
use Magento\FunctionalTestingFramework\Suite\Objects\SuiteObject;
12+
use Magento\FunctionalTestingFramework\Suite\Parsers\SuiteDataParser;
13+
use Magento\FunctionalTestingFramework\Test\Handlers\CestObjectHandler;
14+
use Magento\FunctionalTestingFramework\Test\Objects\CestObject;
15+
use Magento\Ui\Test\Unit\Component\PagingTest;
16+
17+
/**
18+
* Class SuiteObjectHandler
19+
*/
20+
class SuiteObjectHandler implements ObjectHandlerInterface
21+
{
22+
const SUITE_TAG_NAME = 'suite';
23+
const INCLUDE_TAG_NAME = 'include';
24+
const EXCLUDE_TAG_NAME = 'exclude';
25+
const MODULE_TAG_NAME = 'module';
26+
const MODULE_TAG_FILE_ATTRIBUTE = 'file';
27+
const CEST_TAG_NAME = 'cest';
28+
const CEST_TAG_NAME_ATTRIBUTE = 'name';
29+
const CEST_TAG_TEST_ATTRIBUTE = 'test';
30+
const GROUP_TAG_NAME = 'group';
31+
32+
/**
33+
* Singleton instance of suite object handler.
34+
*
35+
* @var SuiteObjectHandler
36+
*/
37+
private static $SUITE_OBJECT_HANLDER_INSTANCE;
38+
39+
/**
40+
* Array of suite objects keyed by suite name.
41+
*
42+
* @var array
43+
*/
44+
private $suiteObjects;
45+
46+
private function __construct()
47+
{
48+
// empty constructor
49+
}
50+
51+
/**
52+
* Function to enforce singleton design pattern
53+
*
54+
* @return ObjectHandlerInterface
55+
*/
56+
public static function getInstance()
57+
{
58+
if (self::$SUITE_OBJECT_HANLDER_INSTANCE == null) {
59+
self::$SUITE_OBJECT_HANLDER_INSTANCE = new SuiteObjectHandler();
60+
self::$SUITE_OBJECT_HANLDER_INSTANCE->initSuiteData();
61+
}
62+
63+
return self::$SUITE_OBJECT_HANLDER_INSTANCE;
64+
}
65+
66+
/**
67+
* Function to return a single suite object by name
68+
*
69+
* @param string $objectName
70+
* @return SuiteObject
71+
*/
72+
public function getObject($objectName)
73+
{
74+
if (!array_key_exists($objectName, $this->suiteObjects)) {
75+
trigger_error("Suite ${objectName} is not defined.", E_USER_ERROR);
76+
}
77+
return $this->suiteObjects[$objectName];
78+
}
79+
80+
/**
81+
* Function to return all objects the handler is responsible for
82+
*
83+
* @return array
84+
*/
85+
public function getAllObjects()
86+
{
87+
return $this->suiteObjects;
88+
}
89+
90+
private function initSuiteData()
91+
{
92+
$suiteDataParser = ObjectManagerFactory::getObjectManager()->create(SuiteDataParser::class);
93+
$this->suiteObjects = $this->parseSuiteDataIntoObjects($suiteDataParser->readSuiteData());
94+
}
95+
96+
private function parseSuiteDataIntoObjects($parsedSuiteData)
97+
{
98+
$suiteObjects = [];
99+
foreach ($parsedSuiteData[self::SUITE_TAG_NAME] as $parsedSuiteName => $parsedSuite) {
100+
$includeCests = [];
101+
$excludeCests = [];
102+
103+
$groupCestsToInclude = $parsedSuite[self::INCLUDE_TAG_NAME][0] ?? [];
104+
$groupCestsToExclude = $parsedSuite[self::EXCLUDE_TAG_NAME][0] ?? [];
105+
106+
if (array_key_exists(self::CEST_TAG_NAME, $groupCestsToInclude)) {
107+
$includeCests = $includeCests + $this->extractRelevantTests($groupCestsToInclude[self::CEST_TAG_NAME]);
108+
}
109+
110+
if (array_key_exists(self::CEST_TAG_NAME, $groupCestsToExclude)) {
111+
$excludeCests = $excludeCests + $this->extractRelevantTests($groupCestsToExclude[self::CEST_TAG_NAME]);
112+
}
113+
114+
$includeCests = $includeCests + $this->extractGroups($groupCestsToInclude);
115+
$excludeCests = $excludeCests + $this->extractGroups($groupCestsToExclude);
116+
117+
118+
// get tests by path (dir or file)
119+
if (array_key_exists(self::MODULE_TAG_NAME, $groupCestsToInclude)) {
120+
$includeCests = array_merge(
121+
$includeCests,
122+
$this->extractModuleAndFiles($groupCestsToInclude[self::MODULE_TAG_NAME])
123+
);
124+
}
125+
126+
if (array_key_exists(self::MODULE_TAG_NAME, $groupCestsToExclude)) {
127+
$excludeCests = array_merge(
128+
$excludeCests,
129+
$this->extractModuleAndFiles($groupCestsToExclude[self::MODULE_TAG_NAME])
130+
);
131+
}
132+
133+
// add all cests if include cests is completely empty
134+
if (empty($includeCests)) {
135+
$includeCests = CestObjectHandler::getInstance()->getAllObjects();
136+
}
137+
138+
$suiteObjects[$parsedSuiteName] = new SuiteObject($parsedSuiteName, $includeCests, $excludeCests);
139+
}
140+
141+
return $suiteObjects;
142+
}
143+
144+
private function extractRelevantTests($suiteTestData)
145+
{
146+
$relevantCests = [];
147+
foreach ($suiteTestData as $cestName => $cestInfo) {
148+
$relevantCest = CestObjectHandler::getInstance()->getObject($cestName);
149+
150+
if (array_key_exists(self::CEST_TAG_TEST_ATTRIBUTE, $cestInfo)) {
151+
$relevantTest = $relevantCest->getTests()[$cestInfo[self::CEST_TAG_TEST_ATTRIBUTE]] ?? null;
152+
153+
if (!$relevantTest) {
154+
trigger_error(
155+
"Test " .
156+
$cestInfo[self::CEST_TAG_NAME_ATTRIBUTE] .
157+
" does not exist.",
158+
E_USER_NOTICE
159+
);
160+
continue;
161+
}
162+
163+
$relevantCests[$cestName] = new CestObject(
164+
$relevantCest->getName(),
165+
$relevantCest->getAnnotations(),
166+
[$relevantTest->getName() => $relevantTest],
167+
$relevantCest->getHooks()
168+
);
169+
} else {
170+
$relevantCests[$cestName] = $relevantCest;
171+
}
172+
}
173+
174+
return $relevantCests;
175+
}
176+
177+
private function extractGroups($suiteData)
178+
{
179+
$cestsByGroup = [];
180+
// get tests by group
181+
if (array_key_exists(self::GROUP_TAG_NAME, $suiteData)) {
182+
//loop groups and add to the groupCests
183+
foreach ($suiteData[self::GROUP_TAG_NAME] as $groupName => $groupVal) {
184+
$cestsByGroup = $cestsByGroup + CestObjectHandler::getInstance()->getCestsByGroup($groupName);
185+
}
186+
}
187+
188+
return $cestsByGroup;
189+
}
190+
191+
private function extractModuleAndFiles($suitePathData)
192+
{
193+
$cestsByModule = [];
194+
foreach ($suitePathData as $moduleName => $fileInfo) {
195+
if (empty($fileInfo)) {
196+
$cestsByModule = array_merge($cestsByModule, $this->resolveModulePathCestNames($moduleName));
197+
} else {
198+
$cestObj = $this->resolveFilePathCestName($fileInfo[self::MODULE_TAG_FILE_ATTRIBUTE], $moduleName);
199+
$cestsByModule[$cestObj->getName()] = $cestObj;
200+
}
201+
}
202+
203+
return $cestsByModule;
204+
}
205+
206+
private function resolveFilePathCestName($filename, $moduleName = null)
207+
{
208+
$filepath = $filename;
209+
if (!strstr($filepath, DIRECTORY_SEPARATOR)) {
210+
$filepath = TESTS_MODULE_PATH .
211+
DIRECTORY_SEPARATOR .
212+
$moduleName .
213+
DIRECTORY_SEPARATOR .
214+
'Cest' .
215+
DIRECTORY_SEPARATOR .
216+
$filename;
217+
}
218+
219+
if (!file_exists($filepath)) {
220+
throw new Exception("Could not find file ${filename}");
221+
}
222+
$xml = simplexml_load_file($filepath);
223+
$cestName = (string)$xml->cest->attributes()->name;
224+
225+
return CestObjectHandler::getInstance()->getObject($cestName);
226+
}
227+
228+
private function resolveModulePathCestNames($moduleName)
229+
{
230+
$cestObjects = [];
231+
$xmlFiles = glob(
232+
TESTS_MODULE_PATH .
233+
DIRECTORY_SEPARATOR .
234+
$moduleName .
235+
DIRECTORY_SEPARATOR .
236+
'Cest' .
237+
DIRECTORY_SEPARATOR .
238+
'*.xml'
239+
);
240+
241+
foreach ($xmlFiles as $xmlFile) {
242+
$cestObj = $this->resolveFilePathCestName($xmlFile);
243+
$cestObjects[$cestObj->getName()] = $cestObj;
244+
}
245+
246+
return $cestObjects;
247+
}
248+
}

0 commit comments

Comments
 (0)