Skip to content

Commit dfde8ba

Browse files
authored
Merge pull request #2 from magento-pangolin/sprint-develop
[Pangolin] Deliver changes to mainline framework repo
2 parents bd0996a + e537ce7 commit dfde8ba

18 files changed

+417
-104
lines changed

src/Magento/FunctionalTestingFramework/DataGenerator/Api/ApiExecutor.php

Lines changed: 62 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
* Copyright © Magento, Inc. All rights reserved.
44
* See COPYING.txt for license details.
55
*/
6+
67
namespace Magento\FunctionalTestingFramework\DataGenerator\Api;
78

89
use Magento\FunctionalTestingFramework\DataGenerator\Handlers\DataObjectHandler;
@@ -12,13 +13,15 @@
1213
use Magento\FunctionalTestingFramework\DataGenerator\Objects\JsonElement;
1314
use Magento\FunctionalTestingFramework\DataGenerator\Util\JsonObjectExtractor;
1415
use Magento\FunctionalTestingFramework\Util\ApiClientUtil;
16+
use Magento\Setup\Exception;
1517

1618
/**
1719
* Class ApiExecutor
1820
*/
1921
class ApiExecutor
2022
{
2123
const PRIMITIVE_TYPES = ['string', 'boolean', 'integer', 'double', 'array'];
24+
const EXCEPTION_REQUIRED_DATA = "%s of key \" %s\" in \"%s\" is required by metadata, but was not provided.";
2225

2326
/**
2427
* Describes the operation for the executor ('create','update','delete')
@@ -149,6 +152,7 @@ private function getAuthorizationHeader($authUrl)
149152
* @param EntityDataObject $entityObject
150153
* @param array $jsonArrayMetadata
151154
* @return array
155+
* @throws \Exception
152156
*/
153157
private function convertJsonArray($entityObject, $jsonArrayMetadata)
154158
{
@@ -157,8 +161,10 @@ private function convertJsonArray($entityObject, $jsonArrayMetadata)
157161

158162
foreach ($jsonArrayMetadata as $jsonElement) {
159163
if ($jsonElement->getType() == JsonObjectExtractor::JSON_OBJECT_OBJ_NAME) {
164+
$entityObj = $this->resolveJsonObjectAndEntityData($entityObject, $jsonElement->getValue());
160165
$jsonArray[$jsonElement->getValue()] =
161-
$this->convertJsonArray($entityObject, $jsonElement->getNestedMetadata());
166+
$this->convertJsonArray($entityObj, $jsonElement->getNestedMetadata());
167+
continue;
162168
}
163169

164170
$jsonElementType = $jsonElement->getValue();
@@ -169,20 +175,40 @@ private function convertJsonArray($entityObject, $jsonArrayMetadata)
169175
EntityDataObject::CEST_UNIQUE_VALUE
170176
);
171177

172-
if (array_key_exists($jsonElement->getKey(), $entityObject->getUniquenessData())) {
173-
$uniqueData = $entityObject->getUniquenessDataByName($jsonElement->getKey());
174-
if ($uniqueData === 'suffix') {
175-
$elementData .= (string)self::getSequence($entityObject->getName());
176-
} else {
177-
$elementData = (string)self::getSequence($entityObject->getName())
178-
. $elementData;
178+
// If data was defined at all, attempt to put it into JSON body
179+
// If data was not defined, and element is required, throw exception
180+
// If no data is defined, don't input defaults per primitive into JSON for the data
181+
if ($elementData != null) {
182+
if (array_key_exists($jsonElement->getKey(), $entityObject->getUniquenessData())) {
183+
$uniqueData = $entityObject->getUniquenessDataByName($jsonElement->getKey());
184+
if ($uniqueData === 'suffix') {
185+
$elementData .= (string)self::getSequence($entityObject->getName());
186+
} else {
187+
$elementData = (string)self::getSequence($entityObject->getName()) . $elementData;
188+
}
179189
}
190+
$jsonArray[$jsonElement->getKey()] = $this->castValue($jsonElementType, $elementData);
191+
192+
} elseif ($jsonElement->getRequired()) {
193+
throw new \Exception(sprintf(
194+
ApiExecutor::EXCEPTION_REQUIRED_DATA,
195+
$jsonElement->getType(),
196+
$jsonElement->getKey(),
197+
$this->entityObject->getName()
198+
));
180199
}
181-
182-
$jsonArray[$jsonElement->getKey()] = $this->castValue($jsonElementType, $elementData);
183200
} else {
184201
$entityNamesOfType = $entityObject->getLinkedEntitiesOfType($jsonElementType);
185202

203+
// If an element is required by metadata, but was not provided in the entity, throw an exception
204+
if ($jsonElement->getRequired() && $entityNamesOfType == null) {
205+
throw new \Exception(sprintf(
206+
ApiExecutor::EXCEPTION_REQUIRED_DATA,
207+
$jsonElement->getType(),
208+
$jsonElement->getKey(),
209+
$this->entityObject->getName()
210+
));
211+
}
186212
foreach ($entityNamesOfType as $entityName) {
187213
$jsonDataSubArray = $this->resolveNonPrimitiveElement($entityName, $jsonElement);
188214

@@ -198,6 +224,25 @@ private function convertJsonArray($entityObject, $jsonArrayMetadata)
198224
return $jsonArray;
199225
}
200226

227+
/**
228+
* This function does a comparison of the entity object being matched to the json element. If there is a mismatch in
229+
* type we attempt to use a nested entity, if the entities are properly matched, we simply return the object.
230+
*
231+
* @param EntityDataObject $entityObject
232+
* @param string $jsonElementValue
233+
* @return EntityDataObject|null
234+
*/
235+
private function resolveJsonObjectAndEntityData($entityObject, $jsonElementValue)
236+
{
237+
if ($jsonElementValue != $entityObject->getType()) {
238+
// if we have a mismatch attempt to retrieve linked data and return just the first linkage
239+
$linkName = $entityObject->getLinkedEntitiesOfType($jsonElementValue)[0];
240+
return DataObjectHandler::getInstance()->getObject($linkName);
241+
}
242+
243+
return $entityObject;
244+
}
245+
201246
/**
202247
* Resolves JsonObjects and pre-defined metadata (in other operation.xml file) referenced by the json metadata
203248
*
@@ -209,8 +254,10 @@ private function resolveNonPrimitiveElement($entityName, $jsonElement)
209254
{
210255
$linkedEntityObj = $this->resolveLinkedEntityObject($entityName);
211256

257+
// in array case
212258
if (!empty($jsonElement->getNestedJsonElement($jsonElement->getValue()))
213-
&& $jsonElement->getType() == 'array') {
259+
&& $jsonElement->getType() == 'array'
260+
) {
214261
$jsonSubArray = $this->convertJsonArray(
215262
$linkedEntityObj,
216263
[$jsonElement->getNestedJsonElement($jsonElement->getValue())]
@@ -285,6 +332,7 @@ private static function getSequence($entityName)
285332
}
286333

287334
// @codingStandardsIgnoreStart
335+
288336
/**
289337
* This function takes a string value and its corresponding type and returns the string cast
290338
* into its the type passed.
@@ -304,6 +352,9 @@ private function castValue($type, $value)
304352
$newVal = (integer)$value;
305353
break;
306354
case 'boolean':
355+
if (strtolower($newVal) === 'false') {
356+
return false;
357+
}
307358
$newVal = (boolean)$value;
308359
break;
309360
case 'double':

src/Magento/FunctionalTestingFramework/DataGenerator/Handlers/JsonDefinitionObjectHandler.php

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ class JsonDefinitionObjectHandler implements ObjectHandlerInterface
3737
const ENTITY_OPERATION_OBJECT_KEY = 'key';
3838
const ENTITY_OPERATION_OBJECT_VALUE = 'value';
3939
const ENTITY_OPERATION_JSON_OBJECT = 'jsonObject';
40+
const ENTITY_OPERATION_REQUIRED = 'required';
4041

4142
/**
4243
* Singleton Instance of class
@@ -164,7 +165,8 @@ private function initJsonDefinitions()
164165
$jsonMetadata[] = new JsonElement(
165166
$jsonEntryType[JsonDefinitionObjectHandler::ENTITY_OPERATION_ENTRY_KEY],
166167
$jsonEntryType[JsonDefinitionObjectHandler::ENTITY_OPERATION_ENTRY_VALUE],
167-
JsonDefinitionObjectHandler::ENTITY_OPERATION_ENTRY
168+
JsonDefinitionObjectHandler::ENTITY_OPERATION_ENTRY,
169+
$jsonEntryType[JsonDefinitionObjectHandler::ENTITY_OPERATION_REQUIRED] ?? null
168170
);
169171
}
170172
}
@@ -192,6 +194,7 @@ private function initJsonDefinitions()
192194
$jsonEntryType[JsonDefinitionObjectHandler::ENTITY_OPERATION_ARRAY_KEY],
193195
$value,
194196
$type,
197+
$jsonEntryType[JsonDefinitionObjectHandler::ENTITY_OPERATION_REQUIRED] ?? null,
195198
$jsonSubMetadata
196199
);
197200
}

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

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,20 +43,32 @@ class JsonElement
4343
*/
4444
private $nestedMetadata = [];
4545

46+
/**
47+
* Json required attribute, used to determine if values need to be cast before insertion.
48+
* @var bool
49+
*/
50+
private $required;
51+
4652
/**
4753
* JsonElement constructor.
4854
* @param string $key
4955
* @param string $value
5056
* @param string $type
57+
* @param bool $required
5158
* @param array $nestedElements
52-
* @param array $nestedMetadata
59+
* @param null|array $nestedMetadata
5360
*/
54-
public function __construct($key, $value, $type, $nestedElements = [], $nestedMetadata = null)
61+
public function __construct($key, $value, $type, $required, $nestedElements = [], $nestedMetadata = null)
5562
{
5663
$this->key = $key;
5764
$this->value = $value;
5865
$this->type = $type;
5966
$this->nestedElements = $nestedElements;
67+
if ($required) {
68+
$this->required = true;
69+
} else {
70+
$this->required = false;
71+
}
6072
$this->nestedMetadata = $nestedMetadata;
6173
}
6274

@@ -90,6 +102,16 @@ public function getType()
90102
return $this->type;
91103
}
92104

105+
/**
106+
* Getter for required attribute
107+
*
108+
* @return bool
109+
*/
110+
public function getRequired()
111+
{
112+
return $this->required;
113+
}
114+
93115
/**
94116
* Returns the nested json element based on the type of entity passed
95117
*

src/Magento/FunctionalTestingFramework/DataGenerator/Util/JsonObjectExtractor.php

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ public function extractJsonObject($jsonObjectArray)
5858
if (array_key_exists(JsonObjectExtractor::JSON_OBJECT_OBJ_NAME, $jsonObjectArray)) {
5959
foreach ($jsonObjectArray[JsonObjectExtractor::JSON_OBJECT_OBJ_NAME] as $jsonObject) {
6060
$nestedJsonElement = $this->extractJsonObject($jsonObject);
61-
$nestedJsonElements[$nestedJsonElement->getKey()] = $nestedJsonElement;
61+
$jsonMetadata[] = $nestedJsonElement;
6262
}
6363
}
6464

@@ -71,6 +71,7 @@ public function extractJsonObject($jsonObjectArray)
7171
$jsonDefKey,
7272
$dataType,
7373
JsonObjectExtractor::JSON_OBJECT_OBJ_NAME,
74+
$jsonObjectArray[JsonDefinitionObjectHandler::ENTITY_OPERATION_REQUIRED] ?? null,
7475
$nestedJsonElements,
7576
$jsonMetadata
7677
);
@@ -89,7 +90,8 @@ private function extractJsonEntries(&$jsonMetadata, $jsonEntryArray)
8990
$jsonMetadata[] = new JsonElement(
9091
$jsonEntryType[JsonDefinitionObjectHandler::ENTITY_OPERATION_ENTRY_KEY],
9192
$jsonEntryType[JsonDefinitionObjectHandler::ENTITY_OPERATION_ENTRY_VALUE],
92-
JsonDefinitionObjectHandler::ENTITY_OPERATION_ENTRY
93+
JsonDefinitionObjectHandler::ENTITY_OPERATION_ENTRY,
94+
$jsonEntryType[JsonDefinitionObjectHandler::ENTITY_OPERATION_REQUIRED] ?? null
9395
);
9496
}
9597
}
@@ -122,6 +124,7 @@ private function extractJsonArrays(&$jsonArrayData, $jsonArrayArray)
122124
$jsonEntryType[JsonDefinitionObjectHandler::ENTITY_OPERATION_ARRAY_KEY],
123125
$jsonElementValue,
124126
JsonDefinitionObjectHandler::ENTITY_OPERATION_ARRAY,
127+
$jsonEntryType[JsonDefinitionObjectHandler::ENTITY_OPERATION_REQUIRED] ?? null,
125128
$nestedJsonElements
126129
);
127130
}

src/Magento/FunctionalTestingFramework/DataGenerator/etc/dataOperation.xsd

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,12 +31,14 @@
3131
</xs:choice>
3232
<xs:attribute type="xs:string" name="key"/>
3333
<xs:attribute type="xs:string" name="dataType" use="required"/>
34+
<xs:attribute type="xs:boolean" name="required" use="optional"/>
3435
</xs:complexType>
3536
<xs:complexType name="entry">
3637
<xs:simpleContent>
3738
<xs:extension base="xs:string">
3839
<xs:attribute type="xs:string" name="key" use="required"/>
3940
<xs:attribute type="xs:string" name="type" use="optional"/>
41+
<xs:attribute type="xs:boolean" name="required" use="optional"/>
4042
</xs:extension>
4143
</xs:simpleContent>
4244
</xs:complexType>
@@ -47,6 +49,7 @@
4749
</xs:choice>
4850
<xs:attribute type="xs:string" name="key" use="required"/>
4951
<xs:attribute type="xs:string" name="type" use="optional"/>
52+
<xs:attribute type="xs:boolean" name="required" use="optional"/>
5053
</xs:complexType>
5154
<xs:complexType name="header">
5255
<xs:simpleContent>
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
<?php
2+
/**
3+
* Copyright © Magento, Inc. All rights reserved.
4+
* See COPYING.txt for license details.
5+
*/
6+
7+
namespace Magento\FunctionalTestingFramework\Exceptions;
8+
9+
/**
10+
* Class TestReferenceException
11+
*/
12+
class TestReferenceException extends \Exception
13+
{
14+
/**
15+
* TestReferenceException constructor.
16+
* @param string $message
17+
*/
18+
public function __construct($message)
19+
{
20+
parent::__construct($message);
21+
}
22+
}

src/Magento/FunctionalTestingFramework/Module/MagentoWebDriver.php

Lines changed: 29 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -99,17 +99,6 @@ public function _getCurrentUri()
9999
return Uri::retrieveUri($url);
100100
}
101101

102-
/**
103-
* Returns an array of Elements.
104-
*
105-
* @param string $locator
106-
* @return array
107-
*/
108-
public function findElement($locator)
109-
{
110-
return $this->_findElements($locator);
111-
}
112-
113102
/**
114103
* Login Magento Admin with given username and password.
115104
*
@@ -195,6 +184,9 @@ public function waitForPageLoad($timeout = 15)
195184
{
196185
$this->waitForJS('return document.readyState == "complete"', $timeout);
197186
$this->waitForAjaxLoad($timeout);
187+
$this->waitForElementNotVisible('.loading-mask', 30);
188+
$this->waitForElementNotVisible('.admin_data-grid-loading-mask', 30);
189+
$this->waitForElementNotVisible('.admin__form-loading-mask', 30);
198190
}
199191

200192
/**
@@ -282,6 +274,32 @@ public function scrollToTopOfPage()
282274
$this->executeJS('window.scrollTo(0,0);');
283275
}
284276

277+
/**
278+
* Conditional click for an area that should be visible
279+
*
280+
* @param string $selector
281+
* @param string dependentSelector
282+
* @param bool $visible
283+
*/
284+
public function conditionalClick($selector, $dependentSelector, $visible)
285+
{
286+
$el = $this->_findElements($dependentSelector);
287+
if (sizeof($el) > 1) {
288+
throw new \Exception("more than one element matches selector " . $selector);
289+
}
290+
291+
$clickCondition = null;
292+
if ($visible) {
293+
$clickCondition = !empty($el) && $el[0]->isDisplayed();
294+
} else {
295+
$clickCondition = empty($el) || !$el[0]->isDisplayed();
296+
}
297+
298+
if ($clickCondition) {
299+
$this->click($selector);
300+
}
301+
}
302+
285303
/**
286304
* Override for _failed method in Codeception method. Adds png and html attachments to allure report
287305
* following parent execution of test failure processing.

0 commit comments

Comments
 (0)