diff --git a/dev/tests/verification/Resources/BasicFunctionalCest.txt b/dev/tests/verification/Resources/BasicFunctionalCest.txt
index cef7cd656..f17e73e1e 100644
--- a/dev/tests/verification/Resources/BasicFunctionalCest.txt
+++ b/dev/tests/verification/Resources/BasicFunctionalCest.txt
@@ -75,6 +75,7 @@ class BasicFunctionalCest
$I->dragAndDrop(".functionalTestSelector", ".functionalTestSelector2");
$I->executeJS("someJSFunction");
$I->fillField(".functionalTestSelector", "someInput");
+ $I->fillField(".functionalTestSelector", "0");
$grabAttributeVar = $I->grabAttributeFrom(".functionalTestSelector", "someInput");
$grabCookieVar = $I->grabCookie("grabCookieInput", ['domain' => 'www.google.com']);
$grabUrlVar = $I->grabFromCurrentUrl("/grabCurrentUrl");
diff --git a/dev/tests/verification/Resources/PersistedReplacementCest.txt b/dev/tests/verification/Resources/PersistedReplacementCest.txt
new file mode 100644
index 000000000..3ef3379cf
--- /dev/null
+++ b/dev/tests/verification/Resources/PersistedReplacementCest.txt
@@ -0,0 +1,71 @@
+amGoingTo("create entity that has the mergeKey: createData1");
+ $replacementPerson = DataObjectHandler::getInstance()->getObject("replacementPerson");
+ $this->createData1 = new DataPersistenceHandler($replacementPerson);
+ $this->createData1->createEntity();
+ }
+
+ /**
+ * @Parameter(name = "AcceptanceTester", value="$I")
+ * @param AcceptanceTester $I
+ * @return void
+ */
+ public function PersistedReplacementTest(AcceptanceTester $I)
+ {
+ $I->amGoingTo("create entity that has the mergeKey: testScopeData");
+ $replacementPerson = DataObjectHandler::getInstance()->getObject("replacementPerson");
+ $testScopeData = new DataPersistenceHandler($replacementPerson);
+ $testScopeData->createEntity();
+ $I->amGoingTo("create entity that has the mergeKey: uniqueData");
+ $uniquePerson = DataObjectHandler::getInstance()->getObject("uniquePerson");
+ $uniqueData = new DataPersistenceHandler($uniquePerson);
+ $uniqueData->createEntity();
+ $I->amOnPage("/success/success2.html");
+ $I->amOnPage($testScopeData->getCreatedDataByName('firstname') . ".html");
+ $I->amOnPage($this->createData1->getCreatedDataByName('firstname') . ".html");
+ $I->amOnPage("/" . $testScopeData->getCreatedDataByName('firstname') . "/" . $testScopeData->getCreatedDataByName('lastname') . ".html");
+ $I->amOnPage("/" . $this->createData1->getCreatedDataByName('firstname') . "/" . $this->createData1->getCreatedDataByName('lastname') . ".html");
+ $I->click("#element ." . $testScopeData->getCreatedDataByName('firstname'));
+ $I->click("#" . $testScopeData->getCreatedDataByName('firstname') . " .success");
+ $I->click("#John-Doe ." . $testScopeData->getCreatedDataByName('lastname'));
+ $I->click("#" . $testScopeData->getCreatedDataByName('firstname') . " ." . $testScopeData->getCreatedDataByName('lastname'));
+ $I->click("#" . $this->createData1->getCreatedDataByName('firstname') . " ." . $this->createData1->getCreatedDataByName('lastname'));
+ $I->fillField("#sample", "Hello " . $testScopeData->getCreatedDataByName('firstname') . " " . $testScopeData->getCreatedDataByName('lastname'));
+ $I->fillField("#sample", "Hello " . $this->createData1->getCreatedDataByName('firstname') . " " . $this->createData1->getCreatedDataByName('lastname'));
+ $I->searchAndMultiSelectOption("#selector", [$testScopeData->getCreatedDataByName('lastname')]);
+ $I->searchAndMultiSelectOption("#selector", [$this->createData1->getCreatedDataByName('lastname')]);
+ $I->amOnPage($uniqueData->getCreatedDataByName('firstname') . ".html");
+ $I->amOnPage("/" . $uniqueData->getCreatedDataByName('firstname') . "/" . $uniqueData->getCreatedDataByName('lastname') . ".html");
+ $I->click("#element ." . $uniqueData->getCreatedDataByName('firstname'));
+ $I->click("#" . $uniqueData->getCreatedDataByName('firstname') . " .success");
+ $I->click("#" . $uniqueData->getCreatedDataByName('firstname'));
+ $I->dragAndDrop($uniqueData->getCreatedDataByName('firstname'), $uniqueData->getCreatedDataByName('firstname'));
+ $I->dragAndDrop("#element ." . $uniqueData->getCreatedDataByName('firstname'), "#" . $uniqueData->getCreatedDataByName('firstname') . " .success");
+ }
+}
diff --git a/dev/tests/verification/TestModule/Cest/basicFunctionalCest.xml b/dev/tests/verification/TestModule/Cest/basicFunctionalCest.xml
index 6d2fa6919..33036f3eb 100644
--- a/dev/tests/verification/TestModule/Cest/basicFunctionalCest.xml
+++ b/dev/tests/verification/TestModule/Cest/basicFunctionalCest.xml
@@ -60,6 +60,7 @@
+
diff --git a/dev/tests/verification/TestModule/Cest/persistedReplacementCest.xml b/dev/tests/verification/TestModule/Cest/persistedReplacementCest.xml
new file mode 100644
index 000000000..17a6fba36
--- /dev/null
+++ b/dev/tests/verification/TestModule/Cest/persistedReplacementCest.xml
@@ -0,0 +1,67 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/dev/tests/verification/TestModule/Data/persistedReplacementData.xml b/dev/tests/verification/TestModule/Data/persistedReplacementData.xml
new file mode 100644
index 000000000..faec18687
--- /dev/null
+++ b/dev/tests/verification/TestModule/Data/persistedReplacementData.xml
@@ -0,0 +1,20 @@
+
+
+
+
+
+
+ John
+ Doe
+
+
+ John
+ Doe
+
+
diff --git a/dev/tests/verification/TestModule/Page/SamplePage.xml b/dev/tests/verification/TestModule/Page/SamplePage.xml
new file mode 100644
index 000000000..034e0dd50
--- /dev/null
+++ b/dev/tests/verification/TestModule/Page/SamplePage.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
diff --git a/dev/tests/verification/TestModule/Section/SampleSection.xml b/dev/tests/verification/TestModule/Section/SampleSection.xml
new file mode 100644
index 000000000..789e8dfd3
--- /dev/null
+++ b/dev/tests/verification/TestModule/Section/SampleSection.xml
@@ -0,0 +1,17 @@
+
+
+
+
+
+
diff --git a/dev/tests/verification/Tests/PersistedReplacementGenerationTest.php b/dev/tests/verification/Tests/PersistedReplacementGenerationTest.php
new file mode 100644
index 000000000..ab74c7331
--- /dev/null
+++ b/dev/tests/verification/Tests/PersistedReplacementGenerationTest.php
@@ -0,0 +1,42 @@
+getObject(self::PERSISTED_REPLACEMENT_CEST);
+ $test = TestGenerator::getInstance(null, [$cest]);
+ $test->createAllCestFiles();
+
+ $cestFile = $test->getExportDir() .
+ DIRECTORY_SEPARATOR .
+ self::PERSISTED_REPLACEMENT_CEST .
+ ".php";
+
+ $this->assertTrue(file_exists($cestFile));
+
+ $fileDiffUtil = new FileDiffUtil(
+ self::RESOURCES_PATH . DIRECTORY_SEPARATOR . self::PERSISTED_REPLACEMENT_CEST . ".txt",
+ $cestFile
+ );
+
+ $diffResult = $fileDiffUtil->diffContents();
+ $this->assertNull($diffResult, $diffResult);
+ }
+}
diff --git a/src/Magento/FunctionalTestingFramework/DataGenerator/Handlers/DataObjectHandler.php b/src/Magento/FunctionalTestingFramework/DataGenerator/Handlers/DataObjectHandler.php
index ad60915d2..6f3987559 100644
--- a/src/Magento/FunctionalTestingFramework/DataGenerator/Handlers/DataObjectHandler.php
+++ b/src/Magento/FunctionalTestingFramework/DataGenerator/Handlers/DataObjectHandler.php
@@ -201,7 +201,7 @@ private function parseDataEntities()
$arrayValues[] = $arrayValue[self::ARRAY_ELEMENT_ITEM_VALUE];
}
- $dataValues[$arrayKey] = $arrayValues;
+ $dataValues[strtolower($arrayKey)] = $arrayValues;
}
}
diff --git a/src/Magento/FunctionalTestingFramework/Test/Objects/ActionGroupObject.php b/src/Magento/FunctionalTestingFramework/Test/Objects/ActionGroupObject.php
index c5beb1236..58d832c43 100644
--- a/src/Magento/FunctionalTestingFramework/Test/Objects/ActionGroupObject.php
+++ b/src/Magento/FunctionalTestingFramework/Test/Objects/ActionGroupObject.php
@@ -84,7 +84,7 @@ private function getResolvedActionsWithArgs($arguments, $actionReferenceKey)
// $regexPattern match on: $matches[0] {{section.element(arg.field)}}
// $matches[1] = section.element
// $matches[2] = arg.field
- $regexPattern = '/{{([\w.]+)\(*([\w.$\']+)*\)*}}/';
+ $regexPattern = '/{{([\w.\[\]]+)\(*([\w.$\']+)*\)*}}/';
foreach ($this->parsedActions as $action) {
$varAttributes = array_intersect(self::VAR_ATTRIBUTES, array_keys($action->getCustomActionAttributes()));
@@ -140,10 +140,10 @@ private function replaceAttributeArguments($arguments, $attributeValue, $matches
if (empty($variable)) {
continue;
}
- // Truncate arg.field into arg
+ // Truncate arg.field into arg. If 'Literal' was passed, variableName will be null.
$variableName = strstr($variable, '.', true);
// Check if arguments has a mapping for the given variableName
- if (!array_key_exists($variableName, $arguments)) {
+ if ($variableName == null || !array_key_exists($variableName, $arguments)) {
continue;
}
$isPersisted = strstr($arguments[$variableName], '$');
diff --git a/src/Magento/FunctionalTestingFramework/Util/TestGenerator.php b/src/Magento/FunctionalTestingFramework/Util/TestGenerator.php
index 86682fc2e..f1c2b630a 100644
--- a/src/Magento/FunctionalTestingFramework/Util/TestGenerator.php
+++ b/src/Magento/FunctionalTestingFramework/Util/TestGenerator.php
@@ -391,11 +391,11 @@ private function generateStepsPhp($stepsObject, $stepsData, $hookObject = false)
}
if (isset($customActionAttributes['selector1'])) {
- $selector1 = $this->wrapWithDoubleQuotes($customActionAttributes['selector1']);
+ $selector1 = $this->addUniquenessFunctionCall($customActionAttributes['selector1']);
}
if (isset($customActionAttributes['selector2'])) {
- $selector2 = $this->wrapWithDoubleQuotes($customActionAttributes['selector2']);
+ $selector2 = $this->addUniquenessFunctionCall($customActionAttributes['selector2']);
}
if (isset($customActionAttributes['x'])) {
@@ -876,57 +876,102 @@ private function generateStepsPhp($stepsObject, $stepsData, $hookObject = false)
}
/**
- * Resolves replacement of $input$ and $$input$$ in given string.
- * Can be given a boolean to surround replacement with quote breaking.
+ * Resolves replacement of $input$ and $$input$$ in given function, recursing and replacing individual arguments
+ * Also determines if each argument requires any quote replacement.
* @param string $inputString
- * @param bool $quoteBreak
+ * @param array $args
* @return string
- * @throws \Exception
*/
- private function resolveTestVariable($inputString, $quoteBreak = false)
+ private function resolveTestVariable($inputString, $args)
{
$outputString = $inputString;
- $replaced = false;
- // Check for Cest-scope variables first, stricter regex match.
- preg_match_all("/\\$\\$[\w.\[\]]+\\$\\$/", $outputString, $matches);
- foreach ($matches[0] as $match) {
- $replacement = null;
- $variable = $this->stripAndSplitReference($match, '$$');
- if (count($variable) != 2) {
- throw new \Exception(
- "Invalid Persisted Entity Reference: " . $match .
- ". Hook persisted entity references must follow \$\$entityMergeKey.field\$\$ format."
- );
- }
- $replacement = sprintf("\$this->%s->getCreatedDataByName('%s')", $variable[0], $variable[1]);
- if ($quoteBreak) {
- $replacement = '" . ' . $replacement . ' . "';
- }
- $outputString = str_replace($match, $replacement, $outputString);
- $replaced = true;
+ //Loop through each argument, replace and then replace
+ foreach ($args as $arg) {
+ $outputArg = $arg;
+ // Match on any $$data.key$$ found inside arg, matches[0] will be array of $$data.key$$
+ preg_match_all("/\\$\\$[\w.\[\]]+\\$\\$/", $outputArg, $matches);
+ $this->replaceMatchesIntoArg($matches[0], $outputArg, "$$");
+
+ // Match on any $data.key$ found inside arg, matches[0] will be array of $data.key$
+ preg_match_all("/\\$[\w.\[\]]+\\$/", $outputArg, $matches);
+ $this->replaceMatchesIntoArg($matches[0], $outputArg, "$");
+
+ $outputString = str_replace($arg, $outputArg, $outputString);
}
- // Check Test-scope variables
- preg_match_all("/\\$[\w.\[\]]+\\$/", $outputString, $matches);
- foreach ($matches[0] as $match) {
+ return $outputString;
+ }
+
+ /**
+ * Replaces all matches into given outputArg with. Variable scope determined by delimiter given
+ * @param array $matches
+ * @param string &$outputArg
+ * @param string $delimiter
+ * @return void
+ * @throws \Exception
+ */
+ private function replaceMatchesIntoArg($matches, &$outputArg, $delimiter)
+ {
+ foreach ($matches as $match) {
$replacement = null;
- $variable = $this->stripAndSplitReference($match, '$');
+ $variable = $this->stripAndSplitReference($match, $delimiter);
if (count($variable) != 2) {
throw new \Exception(
- "Invalid Persisted Entity Reference: " . $match .
- ". Test persisted entity references must follow \$entityMergeKey.field\$ format."
+ "Invalid Persisted Entity Reference: {$match}.
+ Test persisted entity references must follow {$delimiter}entityMergeKey.field{$delimiter} format."
);
}
- $replacement = sprintf("$%s->getCreatedDataByName('%s')", $variable[0], $variable[1]);
- if ($quoteBreak) {
- $replacement = '" . ' . $replacement . ' . "';
+ if ($delimiter == "$") {
+ $replacement = sprintf("$%s->getCreatedDataByName('%s')", $variable[0], $variable[1]);
+ } elseif ($delimiter == "$$") {
+ $replacement = sprintf("\$this->%s->getCreatedDataByName('%s')", $variable[0], $variable[1]);
+ }
+
+ //Determine if quoteBreak check is necessary. Assume replacement is surrounded in quotes, then override
+ if (strpos($outputArg, "\"") !== false) {
+ $outputArg = $this->processQuoteBreaks($match, $outputArg, $replacement);
+ } else {
+ $outputArg = str_replace($match, $replacement, $outputArg);
}
- $outputString = str_replace($match, $replacement, $outputString);
- $replaced = true;
}
+ }
- return $outputString;
+ /**
+ * Processes an argument for $data.key$ and determines if it needs quote breaks on either ends.
+ * Returns an output with quote breaks and replacement already done.
+ * @param string $match
+ * @param string $argument
+ * @param string $replacement
+ * @return string
+ */
+ private function processQuoteBreaks($match, $argument, $replacement)
+ {
+ $outputArg = $argument;
+ $beforeIndex = strpos($outputArg, $match) - 1;
+ $afterIndex = $beforeIndex + strlen($match) + 1;
+ $quoteBefore = true;
+ $quoteAfter = true;
+
+ // Prepare replacement with quote breaks if needed
+ if ($argument[$beforeIndex] != "\"") {
+ $replacement = '" . ' . $replacement;
+ $quoteBefore = false;
+ }
+ if ($argument[$afterIndex] != "\"") {
+ $replacement = $replacement . ' . "';
+ $quoteAfter = false;
+ }
+ //Remove quotes at either end of argument if they aren't necessary.
+ if ($quoteBefore) {
+ $outputArg = substr($outputArg, 0, $beforeIndex) . substr($outputArg, $beforeIndex+1);
+ $afterIndex--;
+ }
+ if ($quoteAfter) {
+ $outputArg = substr($outputArg, 0, $afterIndex) . substr($outputArg, $afterIndex+1);
+ }
+ $outputArg = str_replace($match, $replacement, $outputArg);
+ return $outputArg;
}
/**
@@ -1161,7 +1206,7 @@ private function addUniquenessFunctionCall($input)
*/
private function wrapWithDoubleQuotes($input)
{
- if (empty($input)) {
+ if ($input == null) {
return '';
}
//Only replace " with \" so that it doesn't break outer string.
@@ -1226,9 +1271,7 @@ private function wrapFunctionCall($actor, $action, ...$args)
}
$output .= ");\n";
- // TODO put in condiional to prevent unncessary quote break (i.e. there are no strings to be appended to
- // variable call.
- return $this->resolveTestVariable($output, true);
+ return $this->resolveTestVariable($output, $args);
}
/**
@@ -1256,9 +1299,7 @@ private function wrapFunctionCallWithReturnValue($returnVariable, $actor, $actio
}
$output .= ");\n";
- // TODO put in condiional to prevent unncessary quote break (i.e. there are no strings to be appended to
- // variable call.
- return $output = $this->resolveTestVariable($output, true);
+ return $this->resolveTestVariable($output, $args);
}
// @codingStandardsIgnoreEnd
}