diff --git a/docs/commands/mftf.md b/docs/commands/mftf.md
index cf1260b6a..b2c36dae4 100644
--- a/docs/commands/mftf.md
+++ b/docs/commands/mftf.md
@@ -491,11 +491,18 @@ To run specific static check scripts
```bash
vendor/bin/mftf static-checks testDependencies
```
+```bash
+vendor/bin/mftf static-checks actionGroupArguments
+```
+```bash
+vendor/bin/mftf static-checks testDependencies actionGroupArguments
+```
#### Existing static checks
* Test Dependency: Checks that test dependencies do not violate Magento module's composer dependencies.
-
+* Action Group Unused Arguments: Checks that action groups do not have unused arguments.
+
### `upgrade:tests`
Applies all the MFTF major version upgrade scripts to test components in the given path (`test.xml`, `data.xml`, etc).
diff --git a/src/Magento/FunctionalTestingFramework/StaticCheck/ActionGroupArgumentsCheck.php b/src/Magento/FunctionalTestingFramework/StaticCheck/ActionGroupArgumentsCheck.php
new file mode 100644
index 000000000..32c7661fe
--- /dev/null
+++ b/src/Magento/FunctionalTestingFramework/StaticCheck/ActionGroupArgumentsCheck.php
@@ -0,0 +1,211 @@
+).)*/mxs';
+ const ACTIONGROUP_ARGUMENT_REGEX_PATTERN = '/]*name="([^"\']*)/mxs';
+ const ACTIONGROUP_NAME_REGEX_PATTERN = '/getModulesPath();
+
+ $actionGroupXmlFiles = StaticCheckHelper::buildFileList(
+ $allModules,
+ DIRECTORY_SEPARATOR . 'ActionGroup' . DIRECTORY_SEPARATOR
+ );
+
+ $this->errors = $this->findErrorsInFileSet($actionGroupXmlFiles);
+
+ $this->output = StaticCheckHelper::printErrorsToFile(
+ $this->errors,
+ self::ERROR_LOG_FILENAME,
+ self::ERROR_LOG_MESSAGE
+ );
+ }
+
+ /**
+ * Return array containing all errors found after running the execute() function.
+ * @return array
+ */
+ public function getErrors()
+ {
+ return $this->errors;
+ }
+
+ /**
+ * Return string of a short human readable result of the check. For example: "No unused arguments found."
+ * @return string
+ */
+ public function getOutput()
+ {
+ return $this->output;
+ }
+
+ /**
+ * Finds all unused arguments in given set of actionGroup files
+ * @param Finder $files
+ * @return array $testErrors
+ */
+ private function findErrorsInFileSet($files)
+ {
+ $actionGroupErrors = [];
+ foreach ($files as $filePath) {
+ $contents = file_get_contents($filePath);
+ preg_match_all(self::ACTIONGROUP_XML_REGEX_PATTERN, $contents, $actionGroups);
+ $actionGroupToArguments = $this->buildUnusedArgumentList($actionGroups[0]);
+ $actionGroupErrors += $this->setErrorOutput($actionGroupToArguments, $filePath);
+ }
+ return $actionGroupErrors;
+ }
+
+ /**
+ * Builds array of action group => unused arguments
+ * @param array $actionGroups
+ * @return array $actionGroupToArguments
+ */
+ private function buildUnusedArgumentList($actionGroups)
+ {
+ $actionGroupToArguments = [];
+
+ foreach ($actionGroups as $actionGroupXml) {
+ preg_match(self::ACTIONGROUP_NAME_REGEX_PATTERN, $actionGroupXml, $actionGroupName);
+ $unusedArguments = $this->findUnusedArguments($actionGroupXml);
+ if (!empty($unusedArguments)) {
+ $actionGroupToArguments[$actionGroupName[1]] = $unusedArguments;
+ }
+ }
+ return $actionGroupToArguments;
+ }
+
+ /**
+ * Returns unused arguments in an action group
+ * @param string $actionGroupXml
+ * @return array
+ */
+ private function findUnusedArguments($actionGroupXml)
+ {
+ $unusedArguments = [];
+
+ preg_match_all(self::ACTIONGROUP_ARGUMENT_REGEX_PATTERN, $actionGroupXml, $arguments);
+ preg_match(self::ACTIONGROUP_NAME_REGEX_PATTERN, $actionGroupXml, $actionGroupName);
+ try {
+ $actionGroup = ActionGroupObjectHandler::getInstance()->getObject($actionGroupName[1]);
+ } catch (XmlException $e) {
+ }
+ foreach ($arguments[1] as $argument) {
+ //pattern to match all argument references
+ $patterns = [
+ '(\{{2}' . $argument . '(\.[a-zA-Z0-9_\[\]\(\).,\'\/ ]+)?}{2})',
+ '([(,\s\'$$]' . $argument . '(\.[a-zA-Z0-9_$\[\]]+)?[),\s\'])'
+ ];
+ // matches entity references
+ if (preg_match($patterns[0], $actionGroupXml)) {
+ continue;
+ }
+ //matches parametrized references
+ if (preg_match($patterns[1], $actionGroupXml)) {
+ continue;
+ }
+ //for extending action groups, exclude arguments that are also defined in parent action group
+ if ($this->isParentActionGroupArgument($argument, $actionGroup)) {
+ continue;
+ }
+ $unusedArguments[] = $argument;
+ }
+ return $unusedArguments;
+ }
+
+ /**
+ * Checks if the argument is also defined in the parent for extending action groups.
+ * @param string $argument
+ * @param ActionGroupObject $actionGroup
+ * @return boolean
+ */
+ private function isParentActionGroupArgument($argument, $actionGroup)
+ {
+ $parentActionGroupName = $actionGroup->getParentName();
+ if ($parentActionGroupName !== null) {
+ $parentActionGroup = ActionGroupObjectHandler::getInstance()->getObject($parentActionGroupName);
+ $parentArguments = $parentActionGroup->getArguments();
+ foreach ($parentArguments as $parentArgument) {
+ if ($argument === $parentArgument->getName()) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Builds and returns error output for violating references
+ *
+ * @param array $actionGroupToArguments
+ * @param string $path
+ * @return mixed
+ */
+ private function setErrorOutput($actionGroupToArguments, $path)
+ {
+ $actionGroupErrors = [];
+ if (!empty($actionGroupToArguments)) {
+ // Build error output
+ $errorOutput = "\nFile \"{$path->getRealPath()}\"";
+ $errorOutput .= "\ncontains action group(s) with unused arguments.\n\t\t";
+ foreach ($actionGroupToArguments as $actionGroup => $arguments) {
+ $errorOutput .= "\n\t {$actionGroup} has unused argument(s): " . implode(", ", $arguments);
+ }
+ $actionGroupErrors[$path->getRealPath()][] = $errorOutput;
+ }
+ return $actionGroupErrors;
+ }
+}
diff --git a/src/Magento/FunctionalTestingFramework/StaticCheck/StaticCheckHelper.php b/src/Magento/FunctionalTestingFramework/StaticCheck/StaticCheckHelper.php
new file mode 100644
index 000000000..f534544fc
--- /dev/null
+++ b/src/Magento/FunctionalTestingFramework/StaticCheck/StaticCheckHelper.php
@@ -0,0 +1,56 @@
+ $error) {
+ fwrite($fileResource, $error[0] . PHP_EOL);
+ }
+
+ fclose($fileResource);
+ $errorCount = count($errors);
+ $output = $message . ": Errors found across {$errorCount} file(s). Error details output to {$outputPath}";
+
+ return $output;
+ }
+
+ /**
+ * Builds list of all XML files in given modulePaths + path given
+ * @param array $modulePaths
+ * @param string $path
+ * @return Finder
+ */
+ public static function buildFileList($modulePaths, $path)
+ {
+ $finder = new Finder();
+ foreach ($modulePaths as $modulePath) {
+ if (!realpath($modulePath . $path)) {
+ continue;
+ }
+ $finder->files()->in($modulePath . $path)->name("*.xml");
+ }
+ return $finder->files();
+ }
+}
diff --git a/src/Magento/FunctionalTestingFramework/StaticCheck/StaticChecksList.php b/src/Magento/FunctionalTestingFramework/StaticCheck/StaticChecksList.php
index f3cf20739..ffa63389d 100644
--- a/src/Magento/FunctionalTestingFramework/StaticCheck/StaticChecksList.php
+++ b/src/Magento/FunctionalTestingFramework/StaticCheck/StaticChecksList.php
@@ -29,6 +29,7 @@ public function __construct(array $checks = [])
{
$this->checks = [
'testDependencies' => new TestDependencyCheck(),
+ 'actionGroupArguments' => new ActionGroupArgumentsCheck(),
] + $checks;
}
diff --git a/src/Magento/FunctionalTestingFramework/StaticCheck/TestDependencyCheck.php b/src/Magento/FunctionalTestingFramework/StaticCheck/TestDependencyCheck.php
index 19725dc18..3c3ed6a37 100644
--- a/src/Magento/FunctionalTestingFramework/StaticCheck/TestDependencyCheck.php
+++ b/src/Magento/FunctionalTestingFramework/StaticCheck/TestDependencyCheck.php
@@ -32,6 +32,9 @@ class TestDependencyCheck implements StaticCheckInterface
const ACTIONGROUP_REGEX_PATTERN = '/ref=["\']([^\'"]*)/';
const ACTIONGROUP_ARGUMENT_REGEX_PATTERN = '/]*name="([^"\']*)/';
+ const ERROR_LOG_FILENAME = 'mftf-dependency-checks';
+ const ERROR_LOG_MESSAGE = 'MFTF File Dependency Check';
+
/**
* Array of FullModuleName => [dependencies]
* @var array
@@ -113,9 +116,9 @@ public function execute(InputInterface $input)
DIRECTORY_SEPARATOR . 'Data' . DIRECTORY_SEPARATOR,
];
// These files can contain references to other modules.
- $testXmlFiles = $this->buildFileList($allModules, $filePaths[0]);
- $actionGroupXmlFiles = $this->buildFileList($allModules, $filePaths[1]);
- $dataXmlFiles= $this->buildFileList($allModules, $filePaths[2]);
+ $testXmlFiles = StaticCheckHelper::buildFileList($allModules, $filePaths[0]);
+ $actionGroupXmlFiles = StaticCheckHelper::buildFileList($allModules, $filePaths[1]);
+ $dataXmlFiles= StaticCheckHelper::buildFileList($allModules, $filePaths[2]);
$this->errors = [];
$this->errors += $this->findErrorsInFileSet($testXmlFiles);
@@ -123,7 +126,11 @@ public function execute(InputInterface $input)
$this->errors += $this->findErrorsInFileSet($dataXmlFiles);
// hold on to the output and print any errors to a file
- $this->output = $this->printErrorsToFile();
+ $this->output = StaticCheckHelper::printErrorsToFile(
+ $this->errors,
+ self::ERROR_LOG_FILENAME,
+ self::ERROR_LOG_MESSAGE
+ );
}
/**
@@ -413,24 +420,6 @@ private function getModuleDependenciesFromReferences($array)
return $filenames;
}
- /**
- * Builds list of all XML files in given modulePaths + path given
- * @param string $modulePaths
- * @param string $path
- * @return Finder
- */
- private function buildFileList($modulePaths, $path)
- {
- $finder = new Finder();
- foreach ($modulePaths as $modulePath) {
- if (!realpath($modulePath . $path)) {
- continue;
- }
- $finder->files()->in($modulePath . $path)->name("*.xml");
- }
- return $finder->files();
- }
-
/**
* Attempts to find any MFTF entity by its name. Returns null if none are found.
* @param string $name
@@ -461,32 +450,4 @@ private function findEntity($name)
}
return null;
}
-
- /**
- * Prints out given errors to file, and returns summary result string
- * @return string
- */
- private function printErrorsToFile()
- {
- $errors = $this->getErrors();
-
- if (empty($errors)) {
- return "No Dependency errors found.";
- }
-
- $outputPath = getcwd() . DIRECTORY_SEPARATOR . "mftf-dependency-checks.txt";
- $fileResource = fopen($outputPath, 'w');
- $header = "MFTF File Dependency Check:\n";
- fwrite($fileResource, $header);
-
- foreach ($errors as $test => $error) {
- fwrite($fileResource, $error[0] . PHP_EOL);
- }
-
- fclose($fileResource);
- $errorCount = count($errors);
- $output = "Dependency errors found across {$errorCount} file(s). Error details output to {$outputPath}";
-
- return $output;
- }
}