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 }