diff --git a/dev/tests/verification/TestModule/Test/AssertTest.xml b/dev/tests/verification/TestModule/Test/AssertTest.xml index 8cdc724cc..753ccedf1 100644 --- a/dev/tests/verification/TestModule/Test/AssertTest.xml +++ b/dev/tests/verification/TestModule/Test/AssertTest.xml @@ -157,47 +157,157 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + apple + ['orange' => 2, 'apple' => 1] + + + kiwi + ['orange' => 2, 'apple' => 1] + + + [1, 2, 3, 5] + [1, 2] + + + ab + ['item1' => 'a', 'item2' => 'ab'] + + + ['a', 'b'] + 2 + + + [] + + + Copyright © 2013-2017 Magento, Inc. All rights reserved. + text + + + text + Copyright © 2013-2017 Magento, Inc. All rights reserved. + + + 0 + + + /out.txt + + + $text + + + 5 + 2 + + + 5 + 2 + + + 5 + 2 + + + xyz + string + + + 21 + int + + + $text + string + + + 2 + 5 + + + 2 + 5 + + + 2 + 5 + + + bc + ['item1' => 'a', 'item2' => 'ab'] + + + text + bc + + + [1, 2] + + + text + + + 5 + 2 + + + abc + + + text + + + bar + /foo/ + + + tag + log + + + foo + /foo/ + + + bar + bar + + + banana + a + + + apple + a + + + 1 + + + admin__control-text + + + text + User::class + + + 21 + User::class + + + text + + + text + + + text + + + new MyException('exception msg') + function() {$this->doSomethingBad();} + @@ -263,29 +373,29 @@ - - admin__control-text + + admin__control-text - - login[username] + + login[username] - - true + + true - - {required:true} + + {required:true} - - display: none; + + display: none; - - 0 + + 0 - - $createData2.firstname$ + + $createData2.firstname$ - - $$createData1.firstname$$ + + $$createData1.firstname$$ diff --git a/src/Magento/FunctionalTestingFramework/Test/Objects/ActionObject.php b/src/Magento/FunctionalTestingFramework/Test/Objects/ActionObject.php index 4d2d45d1f..0ae3d336e 100644 --- a/src/Magento/FunctionalTestingFramework/Test/Objects/ActionObject.php +++ b/src/Magento/FunctionalTestingFramework/Test/Objects/ActionObject.php @@ -56,10 +56,10 @@ class ActionObject "command", "html" ]; - const OLD_ASSERTION_ATTRIBUTES = ["expected", "expectedType", "actual", "actualType"]; const ASSERTION_ATTRIBUTES = ["expectedResult" => "expected", "actualResult" => "actual"]; const ASSERTION_TYPE_ATTRIBUTE = "type"; const ASSERTION_VALUE_ATTRIBUTE = "value"; + const ASSERTION_ELEMENT_ATTRIBUTES = ["selector", "attribute"]; const DELETE_DATA_MUTUAL_EXCLUSIVE_ATTRIBUTES = ["url", "createDataKey"]; const EXTERNAL_URL_AREA_INVALID_ACTIONS = ['amOnPage']; const FUNCTION_CLOSURE_ACTIONS = ['waitForElementChange']; @@ -303,24 +303,6 @@ public function resolveReferences() public function trimAssertionAttributes() { $actionAttributeKeys = array_keys($this->actionAttributes); - - /** MQE-683 DEPRECATE OLD METHOD HERE - * Checks if action has any of the old, single line attributes - * Throws a warning and returns, assuming old syntax is used. - */ - $oldAttributes = array_intersect($actionAttributeKeys, ActionObject::OLD_ASSERTION_ATTRIBUTES); - if (!empty($oldAttributes)) { - $appConfig = MftfApplicationConfig::getConfig(); - if ($appConfig->getPhase() == MftfApplicationConfig::GENERATION_PHASE && $appConfig->verboseEnabled()) { - LoggingUtil::getInstance()->getLogger(ActionObject::class)->deprecation( - "use of one line Assertion actions will be deprecated in MFTF 3.0.0, please use nested syntax", - ["action" => $this->type, "stepKey" => $this->stepKey], - true - ); - } - return; - } - $relevantKeys = array_keys(ActionObject::ASSERTION_ATTRIBUTES); $relevantAssertionAttributes = array_intersect($actionAttributeKeys, $relevantKeys); @@ -328,47 +310,25 @@ public function trimAssertionAttributes() return; } - $this->validateAssertionSchema($relevantAssertionAttributes); - // Flatten nested Elements's type and value into key=>value entries + // Also, add selector/value attributes if they are present in nested Element foreach ($this->actionAttributes as $key => $subAttributes) { + foreach (self::ASSERTION_ELEMENT_ATTRIBUTES as $ATTRIBUTE) { + if (isset($subAttributes[$ATTRIBUTE])) { + $this->actionAttributes[$ATTRIBUTE] = $subAttributes[$ATTRIBUTE]; + } + } if (in_array($key, $relevantKeys)) { $prefix = ActionObject::ASSERTION_ATTRIBUTES[$key]; $this->actionAttributes[$prefix . ucfirst(ActionObject::ASSERTION_TYPE_ATTRIBUTE)] = - $subAttributes[ActionObject::ASSERTION_TYPE_ATTRIBUTE]; + $subAttributes[ActionObject::ASSERTION_TYPE_ATTRIBUTE] ?? "NO_TYPE"; $this->actionAttributes[$prefix] = - $subAttributes[ActionObject::ASSERTION_VALUE_ATTRIBUTE]; + $subAttributes[ActionObject::ASSERTION_VALUE_ATTRIBUTE] ?? ""; unset($this->actionAttributes[$key]); } } } - /** - * Validates that the given assertion attributes have valid schema according to nested assertion syntax. - * @param array $attributes - * @return void - * @throws TestReferenceException - */ - private function validateAssertionSchema($attributes) - { - /** MQE-683 DEPRECATE OLD METHOD HERE - * Unnecessary validation, only needed for backwards compatibility - */ - $singleChildTypes = ['assertEmpty', 'assertFalse', 'assertFileExists', 'assertFileNotExists', - 'assertIsEmpty', 'assertNotEmpty', 'assertNotNull', 'assertNull', 'assertTrue', - 'assertElementContainsAttribute']; - - if (!in_array($this->type, $singleChildTypes)) { - if (!in_array('expectedResult', $attributes) - || !in_array('actualResult', $attributes)) { - throw new TestReferenceException( - "{$this->type} must have both an expectedResult & actualResult defined (stepKey: {$this->stepKey})", - ["action" => $this->type, "stepKey" => $this->stepKey] - ); - } - } - } - /** * Look up the selector for SomeSectionName.ElementName and set it as the selector attribute in the * resolved custom attributes. Also set the timeout value. diff --git a/src/Magento/FunctionalTestingFramework/Test/etc/Actions/assertActions.xsd b/src/Magento/FunctionalTestingFramework/Test/etc/Actions/assertActions.xsd index 4fdde9990..22f306382 100644 --- a/src/Magento/FunctionalTestingFramework/Test/etc/Actions/assertActions.xsd +++ b/src/Magento/FunctionalTestingFramework/Test/etc/Actions/assertActions.xsd @@ -49,22 +49,6 @@ - - - - Assertion's Expected value. Cast by expectedType. - - - - - - - - Assertion's Actual value. Cast by actualType. - - - - @@ -92,16 +76,11 @@ - - - - - @@ -115,23 +94,8 @@ - + - - - - Assertion's Expected value. Cast by expectedType. - - - - - - - - Attribute in given element to be assert against. - - - @@ -145,10 +109,6 @@ - - - - @@ -163,10 +123,6 @@ - - - - @@ -181,10 +137,6 @@ - - - - @@ -200,10 +152,6 @@ - - - - @@ -218,10 +166,6 @@ - - - - @@ -235,10 +179,6 @@ - - - - @@ -253,10 +193,6 @@ - - - - @@ -271,10 +207,6 @@ - - - - @@ -288,10 +220,6 @@ - - - - @@ -305,10 +233,6 @@ - - - - @@ -323,10 +247,6 @@ - - - - @@ -341,10 +261,6 @@ - - - - @@ -359,10 +275,6 @@ - - - - @@ -377,10 +289,6 @@ - - - - @@ -395,10 +303,6 @@ - - - - @@ -412,10 +316,6 @@ - - - - @@ -430,10 +330,6 @@ - - - - @@ -448,10 +344,6 @@ - - - - @@ -466,10 +358,6 @@ - - - - @@ -484,10 +372,6 @@ - - - - @@ -501,10 +385,6 @@ - - - - @@ -519,10 +399,6 @@ - - - - @@ -538,10 +414,6 @@ - - - - @@ -555,10 +427,6 @@ - - - - @@ -573,10 +441,6 @@ - - - - @@ -591,10 +455,6 @@ - - - - @@ -608,10 +468,6 @@ - - - - @@ -626,10 +482,6 @@ - - - - @@ -644,10 +496,6 @@ - - - - @@ -662,10 +510,6 @@ - - - - @@ -680,10 +524,6 @@ - - - - @@ -697,10 +537,6 @@ - - - - @@ -715,10 +551,6 @@ - - - - @@ -735,6 +567,20 @@ + + + + Element containing the Expected value and selector/attributeName. + + + + + + + + + + diff --git a/src/Magento/FunctionalTestingFramework/Upgrade/UpdateAssertionSchema.php b/src/Magento/FunctionalTestingFramework/Upgrade/UpdateAssertionSchema.php new file mode 100644 index 000000000..09eafa483 --- /dev/null +++ b/src/Magento/FunctionalTestingFramework/Upgrade/UpdateAssertionSchema.php @@ -0,0 +1,159 @@ + actions to be nested as possible + * WILL NOT CATCH cases where style is a mix of old and new + * + * @param InputInterface $input + * @param OutputInterface $output + * @return string + */ + public function execute(InputInterface $input, OutputInterface $output) + { + $testsPath = $input->getArgument('path'); + $finder = new Finder(); + $finder->files()->in($testsPath)->name("*.xml"); + + $fileSystem = new Filesystem(); + $testsUpdated = 0; + foreach ($finder->files() as $file) { + $contents = $file->getContents(); + // Isolate but never ... , stops after finding first /> + preg_match_all('//', $contents, $potentialAssertions); + $newAssertions = []; + $index = 0; + if (empty($potentialAssertions[0])) { + continue; + } + foreach ($potentialAssertions[0] as $potentialAssertion) { + $newAssertions[$index] = $this->convertOldAssertionToNew($potentialAssertion); + $index++; + } + foreach ($newAssertions as $currentIndex => $replacements) { + $contents = str_replace($potentialAssertions[0][$currentIndex], $replacements, $contents); + } + $fileSystem->dumpFile($file->getRealPath(), $contents); + $testsUpdated++; + } + + return ("Assertion Syntax updated in {$testsUpdated} file(s).\n"); + } + + /** + * Takes given string and attempts to convert it from single line to multi-line + * + * @param string $assertion + * @return string + * @SuppressWarnings(PHPMD.CyclomaticComplexity) + * @SuppressWarnings(PHPMD.NPathComplexity) + */ + private function convertOldAssertionToNew($assertion) + { + // assertSomething + $assertType = ltrim(explode(' ', $assertion)[0], '<'); + + // regex to all attribute=>value pairs + $allAttributes = "stepKey|actual|actualType|expected|expectedType|expectedValue|"; + $allAttributes .= "delta|message|selector|attribute|before|after|remove"; + $grabValueRegex = '/('. $allAttributes .')=(\'[^\']*\'|"[^"]*")/'; + + // Makes 3 arrays in $grabbedParts: + // 0 contains stepKey="value" + // 1 contains stepKey + // 2 contains value + $sortedParts = []; + preg_match_all($grabValueRegex, $assertion, $grabbedParts); + for ($i = 0; $i < count($grabbedParts[0]); $i++) { + $sortedParts[$grabbedParts[1][$i]] = $grabbedParts[2][$i]; + } + + // Begin trimming values and adding back into new string + $trimmedParts = []; + $newString = "<$assertType"; + $subElements = ["actual" => [], "expected" => []]; + foreach ($sortedParts as $type => $value) { + // If attribute="'value'", elseif attribute='"value"', new nested format will break if we leave these in + if (strpos($value, '"') === 0) { + $value = rtrim(ltrim($value, '"'), '"'); + } elseif (strpos($value, "'") === 0) { + $value = rtrim(ltrim($value, "'"), "'"); + } + // If value is empty string (" " or ' '), trim again to become empty + if (str_replace(" ", "", $value) == "''") { + $value = ""; + } elseif (str_replace(" ", "", $value) == '""') { + $value = ""; + } + + // Value is ready for storage/reapply + $trimmedParts[$type] = $value; + if (in_array($type, ["stepKey", "delta", "message", "before", "after", "remove"])) { + // Add back as attribute safely + $newString .= " $type=\"$value\""; + continue; + } + + // Store in subtype for child element creation + if ($type == "actual") { + $subElements["actual"]["value"] = $value; + } elseif ($type == "actualType") { + $subElements["actual"]["type"] = $value; + } elseif ($type == "expected" or $type == "expectedValue") { + $subElements["expected"]["value"] = $value; + } elseif ($type == "expectedType") { + $subElements["expected"]["type"] = $value; + } + } + $newString .= ">\n"; + + // Assert type is very edge-cased, completely different schema + if ($assertType == 'assertElementContainsAttribute') { + // assertElementContainsAttribute type defaulted to string if not present + if (!isset($subElements["expected"]['type'])) { + $subElements["expected"]['type'] = "string"; + } + $value = $subElements['expected']['value'] ?? ""; + $type = $subElements["expected"]['type']; + $selector = $trimmedParts['selector']; + $attribute = $trimmedParts['attribute']; + // @codingStandardsIgnoreStart + $newString .= "\t\t\t$value\n"; + // @codingStandardsIgnoreEnd + } else { + // Set type to const if it's absent, old default + if (isset($subElements["actual"]['value']) && !isset($subElements["actual"]['type'])) { + $subElements["actual"]['type'] = "const"; + } + if (isset($subElements["expected"]['value']) && !isset($subElements["expected"]['type'])) { + $subElements["expected"]['type'] = "const"; + } + foreach ($subElements as $type => $subElement) { + if (empty($subElement)) { + continue; + } + $value = $subElement['value']; + $typeValue = $subElement['type']; + $newString .= "\t\t\t<{$type}Result type=\"$typeValue\">$value\n"; + } + } + $newString .= " "; + return $newString; + } +} diff --git a/src/Magento/FunctionalTestingFramework/Upgrade/UpgradeScriptList.php b/src/Magento/FunctionalTestingFramework/Upgrade/UpgradeScriptList.php index b4858241a..6293b589b 100644 --- a/src/Magento/FunctionalTestingFramework/Upgrade/UpgradeScriptList.php +++ b/src/Magento/FunctionalTestingFramework/Upgrade/UpgradeScriptList.php @@ -29,6 +29,7 @@ public function __construct(array $scripts = []) { $this->scripts = [ 'upgradeTestSchema' => new UpdateTestSchemaPaths(), + 'upgradeAssertionSchema' => new UpdateAssertionSchema(), 'renameMetadataFiles' => new RenameMetadataFiles(), 'removeModuleFileInSuiteFiles' => new RemoveModuleFileInSuiteFiles(), 'splitMultipleEntitiesFiles' => new SplitMultipleEntitiesFiles(), diff --git a/src/Magento/FunctionalTestingFramework/Util/TestGenerator.php b/src/Magento/FunctionalTestingFramework/Util/TestGenerator.php index ac367449f..275c01c8f 100644 --- a/src/Magento/FunctionalTestingFramework/Util/TestGenerator.php +++ b/src/Magento/FunctionalTestingFramework/Util/TestGenerator.php @@ -623,9 +623,6 @@ public function generateStepsPhp($actionObjects, $generationScope = TestGenerato } elseif (isset($customActionAttributes['url'])) { $input = $this->addUniquenessFunctionCall($customActionAttributes['url']); $url = $this->addUniquenessFunctionCall($customActionAttributes['url']); - } elseif (isset($customActionAttributes['expectedValue'])) { - //For old Assert backwards Compatibility, remove when deprecating - $assertExpected = $this->addUniquenessFunctionCall($customActionAttributes['expectedValue']); } elseif (isset($customActionAttributes['regex'])) { $input = $this->addUniquenessFunctionCall($customActionAttributes['regex']); }