Skip to content

Commit c44cb51

Browse files
committed
MQE-1234: Allow XML Parser to read XML comment into comment action
1 parent 9b5d79c commit c44cb51

File tree

8 files changed

+235
-20
lines changed

8 files changed

+235
-20
lines changed

dev/tests/verification/Resources/AssertTest.txt

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,9 @@ class AssertTestCest
5252
null
5353
);
5454
$grabTextFrom1 = $I->grabTextFrom(".copyright>span");
55+
$I->comment(" custom asserts ");
5556
$I->assertArrayIsSorted(["1", "2", "3", "4", "5"], "asc");
57+
$I->comment(" asserts without variable replacement ");
5658
$I->comment("asserts without variable replacement");
5759
$I->assertArrayHasKey("apple", ['orange' => 2, 'apple' => 1], "pass");
5860
$I->assertArrayNotHasKey("kiwi", ['orange' => 2, 'apple' => 1], "pass");
@@ -88,6 +90,7 @@ class AssertTestCest
8890
$I->assertStringStartsNotWith("a", "banana", "pass");
8991
$I->assertStringStartsWith("a", "apple", "pass");
9092
$I->assertTrue(true, "pass");
93+
$I->comment(" asserts backward compatible ");
9194
$I->comment("asserts backward compatible");
9295
$I->assertArrayHasKey("apple", ['orange' => 2, 'apple' => 1], "pass");
9396
$I->assertArrayNotHasKey("kiwi", ['orange' => 2, 'apple' => 1], "pass");
@@ -130,15 +133,18 @@ class AssertTestCest
130133
$I->assertIsEmpty($text, "pass");
131134
$I->assertNull($text, "pass");
132135
$I->expectException(new MyException('exception msg'), function() {$this->doSomethingBad();});
136+
$I->comment(" string type that use created data ");
133137
$I->comment("string type that use created data");
134138
$I->assertStringStartsWith("D", PersistedObjectHandler::getInstance()->retrieveEntityField('createData1', 'lastname', 'test') . ", " . PersistedObjectHandler::getInstance()->retrieveEntityField('createData1', 'firstname', 'test'), "fail");
135139
$I->assertStringStartsNotWith("W", PersistedObjectHandler::getInstance()->retrieveEntityField('createData2', 'firstname', 'test') . ", " . PersistedObjectHandler::getInstance()->retrieveEntityField('createData2', 'lastname', 'test'), "pass");
136140
$I->assertEquals(PersistedObjectHandler::getInstance()->retrieveEntityField('createData1', 'lastname', 'test'), PersistedObjectHandler::getInstance()->retrieveEntityField('createData1', 'lastname', 'test'), "pass");
141+
$I->comment(" array type that use created data ");
137142
$I->comment("array type that use created data");
138143
$I->assertArraySubset([PersistedObjectHandler::getInstance()->retrieveEntityField('createData1', 'lastname', 'test'), PersistedObjectHandler::getInstance()->retrieveEntityField('createData1', 'firstname', 'test')], [PersistedObjectHandler::getInstance()->retrieveEntityField('createData1', 'lastname', 'test'), PersistedObjectHandler::getInstance()->retrieveEntityField('createData1', 'firstname', 'test'), "1"], "pass");
139144
$I->assertArraySubset([PersistedObjectHandler::getInstance()->retrieveEntityField('createData2', 'firstname', 'test'), PersistedObjectHandler::getInstance()->retrieveEntityField('createData2', 'lastname', 'test')], [PersistedObjectHandler::getInstance()->retrieveEntityField('createData2', 'firstname', 'test'), PersistedObjectHandler::getInstance()->retrieveEntityField('createData2', 'lastname', 'test'), "1"], "pass");
140145
$I->assertArrayHasKey("lastname", ['lastname' => PersistedObjectHandler::getInstance()->retrieveEntityField('createData1', 'lastname', 'test'), 'firstname' => PersistedObjectHandler::getInstance()->retrieveEntityField('createData1', 'firstname', 'test')], "pass");
141146
$I->assertArrayHasKey("lastname", ['lastname' => PersistedObjectHandler::getInstance()->retrieveEntityField('createData2', 'lastname', 'test'), 'firstname' => PersistedObjectHandler::getInstance()->retrieveEntityField('createData2', 'firstname', 'test')], "pass");
147+
$I->comment(" this section can only be generated and cannot run ");
142148
$I->assertInstanceOf(User::class, $text, "pass");
143149
$I->assertNotInstanceOf(User::class, 21, "pass");
144150
$I->assertFileExists($text, "pass");
@@ -149,6 +155,7 @@ class AssertTestCest
149155
$I->fail("fail");
150156
$I->fail(PersistedObjectHandler::getInstance()->retrieveEntityField('createData2', 'firstname', 'test') . " " . PersistedObjectHandler::getInstance()->retrieveEntityField('createData2', 'lastname', 'test'));
151157
$I->fail(PersistedObjectHandler::getInstance()->retrieveEntityField('createData1', 'firstname', 'test') . " " . PersistedObjectHandler::getInstance()->retrieveEntityField('createData1', 'lastname', 'test'));
158+
$I->comment(" assertElementContainsAttribute examples ");
152159
$I->assertElementContainsAttribute("#username", "class", "admin__control-text");
153160
$I->assertElementContainsAttribute("#username", "name", "login[username]");
154161
$I->assertElementContainsAttribute("#username", "autofocus", "true");
@@ -157,6 +164,7 @@ class AssertTestCest
157164
$I->assertElementContainsAttribute(".admin__menu-overlay", "border", "0");
158165
$I->assertElementContainsAttribute("#username", "value", PersistedObjectHandler::getInstance()->retrieveEntityField('createData2', 'firstname', 'test'));
159166
$I->assertElementContainsAttribute("#username", "value", PersistedObjectHandler::getInstance()->retrieveEntityField('createData1', 'firstname', 'test'));
167+
$I->comment(" assert entity resolution ");
160168
$I->assertEquals("John", "Doe", "pass");
161169
}
162170
}
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
<?php
2+
namespace Magento\AcceptanceTest\_default\Backend;
3+
4+
use Magento\FunctionalTestingFramework\AcceptanceTester;
5+
use Magento\FunctionalTestingFramework\DataGenerator\Handlers\CredentialStore;
6+
use Magento\FunctionalTestingFramework\DataGenerator\Handlers\PersistedObjectHandler;
7+
use \Codeception\Util\Locator;
8+
use Yandex\Allure\Adapter\Annotation\Features;
9+
use Yandex\Allure\Adapter\Annotation\Stories;
10+
use Yandex\Allure\Adapter\Annotation\Title;
11+
use Yandex\Allure\Adapter\Annotation\Description;
12+
use Yandex\Allure\Adapter\Annotation\Parameter;
13+
use Yandex\Allure\Adapter\Annotation\Severity;
14+
use Yandex\Allure\Adapter\Model\SeverityLevel;
15+
use Yandex\Allure\Adapter\Annotation\TestCaseId;
16+
17+
/**
18+
* @Title("[NO TESTCASEID]: CommentTest")
19+
* @group functional
20+
*/
21+
class CommentTestCest
22+
{
23+
/**
24+
* @param AcceptanceTester $I
25+
* @throws \Exception
26+
*/
27+
public function _before(AcceptanceTester $I)
28+
{
29+
$I->comment("< > & \$abc \" abc ' <click stepKey=\"click\" userInput=\"{{UniquePerson.firstname}}\" selector=\"#id\">/");
30+
$I->amGoingTo("create entity that has the stepKey: createDataHook");
31+
PersistedObjectHandler::getInstance()->createEntity(
32+
"createDataHook",
33+
"hook",
34+
"ReplacementPerson",
35+
[],
36+
null
37+
);
38+
$I->comment("<abc>");
39+
}
40+
41+
/**
42+
* @Severity(level = SeverityLevel::CRITICAL)
43+
* @Features({"TestModule"})
44+
* @Stories({"MQE-1234"})
45+
* @Parameter(name = "AcceptanceTester", value="$I")
46+
* @param AcceptanceTester $I
47+
* @return void
48+
* @throws \Exception
49+
*/
50+
public function CommentTest(AcceptanceTester $I)
51+
{
52+
$I->amGoingTo("create entity that has the stepKey: createDataTest");
53+
PersistedObjectHandler::getInstance()->createEntity(
54+
"createDataTest",
55+
"test",
56+
"DefaultPerson",
57+
[],
58+
null
59+
);
60+
$I->comment("<abc");
61+
$I->comment("abc&");
62+
$I->comment(">abc");
63+
$I->comment("Entering Action Group commentActionGroup (commentInTest4)");
64+
$I->comment("xml comment in Action Group before");
65+
$I->comment("commentInActionGroup1");
66+
$I->wait(1);
67+
$I->comment("commentInActionGroup2");
68+
$I->comment("xml comment in Action Group inside");
69+
$I->wait(1);
70+
$I->comment("xml comment in Action Group after");
71+
$I->comment("Exiting Action Group commentActionGroup (commentInTest4)");
72+
$I->comment("< > & \$abc \" abc ' <click stepKey=\"click\" userInput=\"\$\$createDataHook.firstname\$\$\" selector=\"#id\">/");
73+
$I->comment(PersistedObjectHandler::getInstance()->retrieveEntityField('createDataHook', 'firstname', 'test'));
74+
$I->comment(PersistedObjectHandler::getInstance()->retrieveEntityField('createDataTest', 'firstname', 'test'));
75+
$I->comment("John" . msq("UniquePerson"));
76+
$I->comment("< > & \$abc \" abc ' <click stepKey=\"click\" userInput=\"\$createDataTest.firstname\$\" selector=\"#id\">/");
77+
$I->comment("");
78+
$I->comment("");
79+
$I->skipReadinessCheck(true);
80+
$I->comment("skipReadiness");
81+
$I->skipReadinessCheck(false);
82+
}
83+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
<?xml version="1.0" encoding="UTF-8" ?>
2+
<!--
3+
/**
4+
* Copyright © Magento, Inc. All rights reserved.
5+
* See COPYING.txt for license details.
6+
*/
7+
-->
8+
<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
9+
xsi:noNamespaceSchemaLocation="../../../../../src/Magento/FunctionalTestingFramework/Test/etc/actionGroupSchema.xsd">
10+
<actionGroup name="commentActionGroup">
11+
<!--xml comment in Action Group before-->
12+
<comment userInput="commentInActionGroup1" stepKey="commentInActionGroup1" />
13+
<wait time="1" stepKey="waitInActionGroup1"/>
14+
<comment userInput="commentInActionGroup2" stepKey="commentInActionGroup2" />
15+
<!--xml comment in Action Group inside-->
16+
<wait time="1" stepKey="waitInActionGroup2"/>
17+
<!--xml comment in Action Group after-->
18+
</actionGroup>
19+
</actionGroups>
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<!--
3+
/**
4+
* Copyright © Magento, Inc. All rights reserved.
5+
* See COPYING.txt for license details.
6+
*/
7+
-->
8+
9+
<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
10+
xsi:noNamespaceSchemaLocation="../../../../../src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd">
11+
<test name="CommentTest">
12+
<annotations>
13+
<severity value="CRITICAL"/>
14+
<title value="CommentTest"/>
15+
<group value="functional"/>
16+
<features value="CommentTest"/>
17+
<stories value="MQE-1234"/>
18+
</annotations>
19+
<before>
20+
<!--< > & $abc " abc ' <click stepKey="click" userInput="{{UniquePerson.firstname}}" selector="#id">/-->
21+
<createData entity="ReplacementPerson" stepKey="createDataHook"/>
22+
<comment userInput="&lt;abc&gt;" stepKey="commentInHook"/>
23+
</before>
24+
<createData entity="DefaultPerson" stepKey="createDataTest"/>
25+
<comment userInput="&lt;abc" stepKey="commentInTest1"/>
26+
<comment userInput="abc&amp;" stepKey="commentInTest2"/>
27+
<comment userInput="&gt;abc" stepKey="commentInTest3"/>
28+
<actionGroup ref="commentActionGroup" stepKey="commentInTest4"/>
29+
<!--< > & $abc " abc ' <click stepKey="click" userInput="$$createDataHook.firstname$$" selector="#id">/-->
30+
<comment userInput="$$createDataHook.firstname$$" stepKey="commentInTest5"/>
31+
<comment userInput="$createDataTest.firstname$" stepKey="commentInTest6"/>
32+
<comment userInput="{{UniquePerson.firstname}}" stepKey="commentInTest8"/>
33+
<!--< > & $abc " abc ' <click stepKey="click" userInput="$createDataTest.firstname$" selector="#id">/-->
34+
<comment stepKey="commentInTest9" userInput="{{emptyData.noData}}"/>
35+
<comment stepKey="commentInTest10" userInput="{{emptyData.definitelyNoData}}"/>
36+
<comment stepKey="commentInTest11" userInput="skipReadiness" skipReadiness="true"/>
37+
</test>
38+
</tests>
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+
namespace tests\verification\Tests;
7+
8+
use tests\util\MftfTestCase;
9+
10+
class CommentGenerationTest extends MftfTestCase
11+
{
12+
/**
13+
* Tests flat generation of a hardcoded test file with no external references.
14+
*
15+
* @throws \Exception
16+
* @throws \Magento\FunctionalTestingFramework\Exceptions\TestReferenceException
17+
*/
18+
public function testCommentGeneration()
19+
{
20+
$this->generateAndCompareTest('CommentTest');
21+
}
22+
}

src/Magento/FunctionalTestingFramework/Test/Config/Converter/Dom/Flat.php

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ class Flat implements ConverterInterface
1818
const REMOVE_KEY_ATTRIBUTE = 'keyForRemoval';
1919
const EXTENDS_ATTRIBUTE = 'extends';
2020
const TEST_HOOKS = ['before', 'after'];
21+
const COMMENT_ACTION = 'comment';
22+
const XML_COMMENT_ACTION = 'xmlComment';
2123

2224
/**
2325
* Array node configuration.
@@ -119,6 +121,23 @@ public function convertXml(\DOMNode $source, $basePath = '')
119121
} else {
120122
$value[$nodeName] = $nodeData;
121123
}
124+
// A new "xmlComment" node is inserted when xml comment is found in non-annotation portion of tests
125+
// or in non-arguments portion of action groups
126+
} elseif ($node->nodeType == XML_COMMENT_NODE) {
127+
if ( (strpos($basePath, '/tests/test') !== false
128+
&& strpos($basePath, '/tests/test/annotations') === false)
129+
|| (strpos($basePath, '/actionGroups/actionGroup') !== false
130+
&& strpos($basePath, '/actionGroups/actionGroup/arguments') === false) ) {
131+
$nodePath = $basePath . '/' . self::COMMENT_ACTION;
132+
$arrayKeyAttribute = $this->arrayNodeConfig->getAssocArrayKeyAttribute($nodePath);
133+
$arrayKeyValue = self::COMMENT_ACTION . uniqid();
134+
$nodeData = [
135+
"nodeName" => self::XML_COMMENT_ACTION,
136+
"userInput" => $node->nodeValue,
137+
$arrayKeyAttribute => $arrayKeyValue,
138+
];
139+
$value[$arrayKeyValue] = $nodeData;
140+
}
122141
} elseif ($node->nodeType == XML_CDATA_SECTION_NODE
123142
|| ($node->nodeType == XML_TEXT_NODE && trim($node->nodeValue) != '')
124143
) {

src/Magento/FunctionalTestingFramework/Test/Objects/ActionObject.php

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ class ActionObject
7272
const DEFAULT_WAIT_TIMEOUT = 10;
7373
const ACTION_ATTRIBUTE_USERINPUT = 'userInput';
7474
const ACTION_TYPE_COMMENT = 'comment';
75+
const ACTION_TYPE_XML_COMMENT = 'xmlComment';
7576

7677
/**
7778
* The unique identifier for the action
@@ -191,7 +192,7 @@ public function getType()
191192
/**
192193
* Getter for actionOrigin
193194
*
194-
* @return string
195+
* @return array
195196
*/
196197
public function getActionOrigin()
197198
{
@@ -267,7 +268,7 @@ public function setTimeout($timeout)
267268
*/
268269
public function resolveReferences()
269270
{
270-
if (empty($this->resolvedCustomAttributes)) {
271+
if (empty($this->resolvedCustomAttributes) && $this->getType() != self::ACTION_TYPE_XML_COMMENT) {
271272
$this->trimAssertionAttributes();
272273
$this->resolveSelectorReferenceAndTimeout();
273274
$this->resolveUrlReference();

src/Magento/FunctionalTestingFramework/Util/TestGenerator.php

Lines changed: 43 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -542,19 +542,23 @@ public function generateStepsPhp($actionObjects, $generationScope = TestGenerato
542542
$sortOrder = $customActionAttributes['sortOrder'];
543543
}
544544

545-
if (isset($customActionAttributes['userInput']) && isset($customActionAttributes['url'])) {
546-
$input = $this->addUniquenessFunctionCall($customActionAttributes['userInput']);
547-
$url = $this->addUniquenessFunctionCall($customActionAttributes['url']);
545+
if ($actionObject->getType() != ActionObject::ACTION_TYPE_XML_COMMENT) {
546+
if (isset($customActionAttributes['userInput']) && isset($customActionAttributes['url'])) {
547+
$input = $this->addUniquenessFunctionCall($customActionAttributes['userInput']);
548+
$url = $this->addUniquenessFunctionCall($customActionAttributes['url']);
549+
} elseif (isset($customActionAttributes['userInput'])) {
550+
$input = $this->addUniquenessFunctionCall($customActionAttributes['userInput']);
551+
} elseif (isset($customActionAttributes['url'])) {
552+
$input = $this->addUniquenessFunctionCall($customActionAttributes['url']);
553+
$url = $this->addUniquenessFunctionCall($customActionAttributes['url']);
554+
} elseif (isset($customActionAttributes['expectedValue'])) {
555+
//For old Assert backwards Compatibility, remove when deprecating
556+
$assertExpected = $this->addUniquenessFunctionCall($customActionAttributes['expectedValue']);
557+
} elseif (isset($customActionAttributes['regex'])) {
558+
$input = $this->addUniquenessFunctionCall($customActionAttributes['regex']);
559+
}
548560
} elseif (isset($customActionAttributes['userInput'])) {
549-
$input = $this->addUniquenessFunctionCall($customActionAttributes['userInput']);
550-
} elseif (isset($customActionAttributes['url'])) {
551-
$input = $this->addUniquenessFunctionCall($customActionAttributes['url']);
552-
$url = $this->addUniquenessFunctionCall($customActionAttributes['url']);
553-
} elseif (isset($customActionAttributes['expectedValue'])) {
554-
//For old Assert backwards Compatibility, remove when deprecating
555-
$assertExpected = $this->addUniquenessFunctionCall($customActionAttributes['expectedValue']);
556-
} elseif (isset($customActionAttributes['regex'])) {
557-
$input = $this->addUniquenessFunctionCall($customActionAttributes['regex']);
561+
$input = $this->escapeStringInDoubleQuotes($customActionAttributes['userInput']);
558562
}
559563

560564
if (isset($customActionAttributes['date']) && isset($customActionAttributes['format'])) {
@@ -1286,6 +1290,13 @@ public function generateStepsPhp($actionObjects, $generationScope = TestGenerato
12861290
case "skipReadinessCheck":
12871291
$testSteps .= $this->wrapFunctionCall($actor, $actionObject, $customActionAttributes['state']);
12881292
break;
1293+
case ActionObject::ACTION_TYPE_XML_COMMENT:
1294+
$testSteps .= sprintf(
1295+
"\t\t$%s->comment(%s);\n",
1296+
$actor,
1297+
$input
1298+
);
1299+
break;
12891300
default:
12901301
$testSteps .= $this->wrapFunctionCall(
12911302
$actor,
@@ -1768,15 +1779,13 @@ private function addDollarSign($input)
17681779
* Wrap parameters into a function call.
17691780
*
17701781
* @param string $actor
1771-
* @param actionObject $action
1772-
* @param string $scope
1782+
* @param ActionObject $action
17731783
* @param array ...$args
17741784
* @return string
17751785
* @throws \Exception
17761786
*/
17771787
private function wrapFunctionCall($actor, $action, ...$args)
17781788
{
1779-
$isFirst = true;
17801789
$output = sprintf("\t\t$%s->%s(", $actor, $action->getType());
17811790
for ($i = 0; $i < count($args); $i++) {
17821791
if (null === $args[$i]) {
@@ -1800,15 +1809,13 @@ private function wrapFunctionCall($actor, $action, ...$args)
18001809
*
18011810
* @param string $returnVariable
18021811
* @param string $actor
1803-
* @param string $action
1804-
* @param string $scope
1812+
* @param ActionObject $action
18051813
* @param array ...$args
18061814
* @return string
18071815
* @throws \Exception
18081816
*/
18091817
private function wrapFunctionCallWithReturnValue($returnVariable, $actor, $action, ...$args)
18101818
{
1811-
$isFirst = true;
18121819
$output = sprintf("\t\t$%s = $%s->%s(", $returnVariable, $actor, $action->getType());
18131820
for ($i = 0; $i < count($args); $i++) {
18141821
if (null === $args[$i]) {
@@ -1880,6 +1887,24 @@ private function resolveAllRuntimeReferences($args)
18801887
return $argResult;
18811888
}
18821889

1890+
/**
1891+
* Escape input string within a pair of double quotes
1892+
*
1893+
* @param string $input
1894+
* @return string
1895+
*/
1896+
private function escapeStringInDoubleQuotes($input)
1897+
{
1898+
if ($input == null) {
1899+
return '';
1900+
}
1901+
// Replace " with \"
1902+
$input = str_replace('"', '\"', $input);
1903+
// Replace $ with \$
1904+
$input = str_replace('$', '\$', $input);
1905+
return sprintf('"%s"', $input);
1906+
}
1907+
18831908
/**
18841909
* Validates parameter array format, making sure user has enclosed string with square brackets.
18851910
*

0 commit comments

Comments
 (0)