diff --git a/dev/tests/_bootstrap.php b/dev/tests/_bootstrap.php index 059441721..9d3750ec6 100644 --- a/dev/tests/_bootstrap.php +++ b/dev/tests/_bootstrap.php @@ -9,9 +9,11 @@ $vendorAutoloadPath = realpath(PROJECT_ROOT . '/vendor/autoload.php'); $mftfTestCasePath = realpath(PROJECT_ROOT . '/dev/tests/util/MftfTestCase.php'); +$mftfStaticTestCasePath = realpath(PROJECT_ROOT . '/dev/tests/util/MftfStaticTestCase.php'); require_once $vendorAutoloadPath; require_once $mftfTestCasePath; +require_once $mftfStaticTestCasePath; // Set up AspectMock $kernel = \AspectMock\Kernel::getInstance(); diff --git a/dev/tests/util/MftfStaticTestCase.php b/dev/tests/util/MftfStaticTestCase.php new file mode 100644 index 000000000..708561031 --- /dev/null +++ b/dev/tests/util/MftfStaticTestCase.php @@ -0,0 +1,49 @@ +getMockBuilder(InputInterface::class) + ->disableOriginalConstructor() + ->getMock(); + if ($path) { + $input->method('getOption') + ->with('path') + ->willReturn($path); + } + return $input; + } + + public function tearDown(): void + { + DirSetupUtil::rmdirRecursive(self::STATIC_RESULTS_DIR); + } +} diff --git a/dev/tests/verification/PauseCheckModule/ActionGroup/ActionGroupWithMultiplePausesActionGroup.xml b/dev/tests/verification/PauseCheckModule/ActionGroup/ActionGroupWithMultiplePausesActionGroup.xml new file mode 100644 index 000000000..5e9299631 --- /dev/null +++ b/dev/tests/verification/PauseCheckModule/ActionGroup/ActionGroupWithMultiplePausesActionGroup.xml @@ -0,0 +1,18 @@ + + + + + + + + + + + + diff --git a/dev/tests/verification/PauseCheckModule/ActionGroup/ActionGroupWithNoPauseActionGroup.xml b/dev/tests/verification/PauseCheckModule/ActionGroup/ActionGroupWithNoPauseActionGroup.xml new file mode 100644 index 000000000..8acae47e8 --- /dev/null +++ b/dev/tests/verification/PauseCheckModule/ActionGroup/ActionGroupWithNoPauseActionGroup.xml @@ -0,0 +1,15 @@ + + + + + + + + + diff --git a/dev/tests/verification/PauseCheckModule/ActionGroup/ActionGroupWithPauseActionGroup.xml b/dev/tests/verification/PauseCheckModule/ActionGroup/ActionGroupWithPauseActionGroup.xml new file mode 100644 index 000000000..aaef1befa --- /dev/null +++ b/dev/tests/verification/PauseCheckModule/ActionGroup/ActionGroupWithPauseActionGroup.xml @@ -0,0 +1,15 @@ + + + + + + + + + diff --git a/dev/tests/verification/PauseCheckModule/Suite/suiteWithMultiplePauseActionsSuite.xml b/dev/tests/verification/PauseCheckModule/Suite/suiteWithMultiplePauseActionsSuite.xml new file mode 100644 index 000000000..47ff1088b --- /dev/null +++ b/dev/tests/verification/PauseCheckModule/Suite/suiteWithMultiplePauseActionsSuite.xml @@ -0,0 +1,28 @@ + + + + + + + + + + + + dataHere + + + + + + + + + + + diff --git a/dev/tests/verification/PauseCheckModule/Suite/suiteWithPauseActionSuite.xml b/dev/tests/verification/PauseCheckModule/Suite/suiteWithPauseActionSuite.xml new file mode 100644 index 000000000..71a3b5769 --- /dev/null +++ b/dev/tests/verification/PauseCheckModule/Suite/suiteWithPauseActionSuite.xml @@ -0,0 +1,27 @@ + + + + + + + + + + + + dataHere + + + + + + + + + + diff --git a/dev/tests/verification/PauseCheckModule/Test/TestWithMultiplePauseActionsTest.xml b/dev/tests/verification/PauseCheckModule/Test/TestWithMultiplePauseActionsTest.xml new file mode 100644 index 000000000..fa47e976c --- /dev/null +++ b/dev/tests/verification/PauseCheckModule/Test/TestWithMultiplePauseActionsTest.xml @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/dev/tests/verification/PauseCheckModule/Test/TestWithPauseActionTest.xml b/dev/tests/verification/PauseCheckModule/Test/TestWithPauseActionTest.xml new file mode 100644 index 000000000..70d0903b8 --- /dev/null +++ b/dev/tests/verification/PauseCheckModule/Test/TestWithPauseActionTest.xml @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + diff --git a/dev/tests/verification/Resources/StaticChecks/mftf-pause-action-usage-checks.txt b/dev/tests/verification/Resources/StaticChecks/mftf-pause-action-usage-checks.txt new file mode 100644 index 000000000..fd042a24c --- /dev/null +++ b/dev/tests/verification/Resources/StaticChecks/mftf-pause-action-usage-checks.txt @@ -0,0 +1,30 @@ + +File "/verification/PauseCheckModule/ActionGroup/ActionGroupWithMultiplePausesActionGroup.xml" +contains pause action(s): + + ActionGroupWithMultiplePausesActionGroup has pause action at stepKey(s): pauseAfterFillField1, pauseAfterFillField2, pauseAfterFillField3 + +File "/verification/PauseCheckModule/ActionGroup/ActionGroupWithPauseActionGroup.xml" +contains pause action(s): + + ActionGroupWithPauseActionGroup has pause action at stepKey(s): pauseAfterFillField2 + +File "/verification/PauseCheckModule/Test/TestWithMultiplePauseActionsTest.xml" +contains pause action(s): + + TestWithMultiplePauseActionsTest has pause action at stepKey(s): pauseBeforeAmOnPageKey, pauseAfterStep2, pauseAfterAmOnPageKey + +File "/verification/PauseCheckModule/Test/TestWithPauseActionTest.xml" +contains pause action(s): + + TestWithPauseActionTest has pause action at stepKey(s): pauseAfterStep3 + +File "/verification/PauseCheckModule/Suite/suiteWithMultiplePauseActionsSuite.xml" +contains pause action(s): + + suiteWithMultiplePauseActionsSuite has pause action at stepKey(s): pauseCreate1, pauseFillAfter + +File "/verification/PauseCheckModule/Suite/suiteWithPauseActionSuite.xml" +contains pause action(s): + + suiteWithPauseActionSuite has pause action at stepKey(s): pauseSuite diff --git a/dev/tests/verification/Tests/StaticCheck/DeprecationStaticCheckTest.php b/dev/tests/verification/Tests/StaticCheck/DeprecationStaticCheckTest.php index bafe586ac..f6aef65f6 100644 --- a/dev/tests/verification/Tests/StaticCheck/DeprecationStaticCheckTest.php +++ b/dev/tests/verification/Tests/StaticCheck/DeprecationStaticCheckTest.php @@ -8,18 +8,11 @@ use AspectMock\Test as AspectMock; use Magento\FunctionalTestingFramework\StaticCheck\DeprecatedEntityUsageCheck; use Magento\FunctionalTestingFramework\StaticCheck\StaticChecksList; -use Magento\FunctionalTestingFramework\Util\Filesystem\DirSetupUtil; -use PHPUnit\Framework\TestCase; use Symfony\Component\Console\Input\InputInterface; +use tests\util\MftfStaticTestCase; -class DeprecationStaticCheckTest extends TestCase +class DeprecationStaticCheckTest extends MftfStaticTestCase { - const STATIC_RESULTS_DIR = TESTS_MODULE_PATH . - DIRECTORY_SEPARATOR . - '_output' . - DIRECTORY_SEPARATOR . - 'static-results'; - const LOG_FILE = self::STATIC_RESULTS_DIR . DIRECTORY_SEPARATOR . DeprecatedEntityUsageCheck::ERROR_LOG_FILENAME . @@ -30,20 +23,6 @@ class DeprecationStaticCheckTest extends TestCase 'DeprecationCheckModule'. DIRECTORY_SEPARATOR; - const RESOURCES_PATH = TESTS_MODULE_PATH . - DIRECTORY_SEPARATOR . - "Resources" . - DIRECTORY_SEPARATOR . - 'StaticChecks'; - - public static function setUpBeforeClass(): void - { - // remove static-results if it exists - if (file_exists(self::STATIC_RESULTS_DIR)) { - DirSetupUtil::rmdirRecursive(self::STATIC_RESULTS_DIR); - } - } - /** * test static-check DeprecatedEntityUsageCheck. * @@ -68,25 +47,4 @@ public function testDeprecatedEntityUsageCheck() self::LOG_FILE ); } - - /** - * Sets input interface - * @param $path - * @return \PHPUnit\Framework\MockObject\MockObject - */ - public function mockInputInterface($path) - { - $input = $this->getMockBuilder(InputInterface::class) - ->disableOriginalConstructor() - ->getMock(); - $input->method('getOption') - ->with('path') - ->willReturn($path); - return $input; - } - - public function tearDown(): void - { - DirSetupUtil::rmdirRecursive(self::STATIC_RESULTS_DIR); - } } diff --git a/dev/tests/verification/Tests/StaticCheck/PauseActionStaticCheckTest.php b/dev/tests/verification/Tests/StaticCheck/PauseActionStaticCheckTest.php new file mode 100644 index 000000000..7c8f77c2f --- /dev/null +++ b/dev/tests/verification/Tests/StaticCheck/PauseActionStaticCheckTest.php @@ -0,0 +1,51 @@ +mockInputInterface(self::TEST_MODULE_PATH); + AspectMock::double(StaticChecksList::class, ['getErrorFilesPath' => self::STATIC_RESULTS_DIR]); + + /** @var InputInterface $input */ + $staticCheck->execute($input); + + $this->assertTrue(file_exists(self::LOG_FILE)); + $this->assertFileEquals( + self::RESOURCES_PATH. + DIRECTORY_SEPARATOR . + PauseActionUsageCheck::ERROR_LOG_FILENAME . + ".txt", + self::LOG_FILE + ); + } +} diff --git a/docs/commands/mftf.md b/docs/commands/mftf.md index 3c170e5ca..0c5c86285 100644 --- a/docs/commands/mftf.md +++ b/docs/commands/mftf.md @@ -449,7 +449,7 @@ vendor/bin/mftf static-checks []... | Option | Description | |-----------------------|-----------------------------------------------------------------------------------------------------------| -| `-p, --path` | Path to a MFTF test module to run "deprecatedEntityUsage" static check script. Option is ignored by other static check scripts. +| `-p, --path` | Path to a MFTF test module to run "deprecatedEntityUsage" and "pauseActionUsage" static check scripts. Option is ignored by other static check scripts. #### Examples @@ -479,6 +479,10 @@ vendor/bin/mftf static-checks actionGroupArguments vendor/bin/mftf static-checks deprecatedEntityUsage ``` +```bash +vendor/bin/mftf static-checks pauseActionUsage +``` + ```bash vendor/bin/mftf static-checks annotations ``` @@ -487,6 +491,10 @@ vendor/bin/mftf static-checks annotations vendor/bin/mftf static-checks deprecatedEntityUsage -p path/to/mftf/test/module ``` +```bash +vendor/bin/mftf static-checks pauseActionUsage -p path/to/mftf/test/module +``` + ```bash vendor/bin/mftf static-checks testDependencies actionGroupArguments ``` @@ -499,6 +507,7 @@ vendor/bin/mftf static-checks testDependencies actionGroupArguments |`actionGroupArguments` | Checks that action groups do not have unused arguments.| |`deprecatedEntityUsage`| Checks that deprecated test entities are not being referenced.| |`annotations`| Checks various details of test annotations, such as missing annotations or duplicate annotations.| +|`pauseUsage`| Checks that pause action is not used in action groups, tests or suites.| #### Defining ruleset diff --git a/src/Magento/FunctionalTestingFramework/Console/StaticChecksCommand.php b/src/Magento/FunctionalTestingFramework/Console/StaticChecksCommand.php index fe07dbaaf..806508ef8 100644 --- a/src/Magento/FunctionalTestingFramework/Console/StaticChecksCommand.php +++ b/src/Magento/FunctionalTestingFramework/Console/StaticChecksCommand.php @@ -151,12 +151,17 @@ private function validateInput(InputInterface $input) } if ($input->getOption('path')) { - if ( (count($this->staticCheckObjects) !== 1) - || array_keys($this->staticCheckObjects)[0] !== StaticChecksList::DEPRECATED_ENTITY_USAGE_CHECK_NAME ) + if ((count($this->staticCheckObjects) !== 1) + || !in_array( + array_keys($this->staticCheckObjects)[0], + [ + StaticChecksList::DEPRECATED_ENTITY_USAGE_CHECK_NAME, + StaticChecksList::PAUSE_ACTION_USAGE_CHECK_NAME + ] + ) + ) throw new InvalidArgumentException( - '--path option can only be used for "' - . StaticChecksList::DEPRECATED_ENTITY_USAGE_CHECK_NAME - . '".' + '--path option is not supported for the command."' ); } } diff --git a/src/Magento/FunctionalTestingFramework/StaticCheck/PauseActionUsageCheck.php b/src/Magento/FunctionalTestingFramework/StaticCheck/PauseActionUsageCheck.php new file mode 100644 index 000000000..e7d106bf0 --- /dev/null +++ b/src/Magento/FunctionalTestingFramework/StaticCheck/PauseActionUsageCheck.php @@ -0,0 +1,229 @@ +scriptUtil = new ScriptUtil(); + $modulePaths = []; + $includeRootPath = true; + $path = $input->getOption('path'); + if ($path) { + if (!realpath($path)) { + throw new \InvalidArgumentException('Invalid --path option: ' . $path); + } + $modulePaths[] = realpath($path); + $includeRootPath = false; + } else { + $modulePaths = $this->scriptUtil->getAllModulePaths(); + } + + $this->testXmlFiles = $this->scriptUtil->getModuleXmlFilesByScope($modulePaths, 'Test'); + $this->actionGroupXmlFiles = $this->scriptUtil->getModuleXmlFilesByScope($modulePaths, 'ActionGroup'); + $this->suiteXmlFiles = $this->scriptUtil->getModuleXmlFilesByScope($modulePaths, 'Suite'); + if ($includeRootPath) { + $this->rootSuiteXmlFiles = $this->scriptUtil->getRootSuiteXmlFiles(); + } + $this->errors = []; + $this->errors += $this->validatePauseActionUsageInActionGroups($this->actionGroupXmlFiles); + $this->errors += $this->validatePauseActionUsageInTests($this->testXmlFiles); + $this->errors += $this->validatePauseActionUsageInSuites($this->suiteXmlFiles); + $this->errors += $this->validatePauseActionUsageInSuites($this->rootSuiteXmlFiles); + + $this->output = $this->scriptUtil->printErrorsToFile( + $this->errors, + StaticChecksList::getErrorFilesPath() . DIRECTORY_SEPARATOR . self::ERROR_LOG_FILENAME . '.txt', + self::ERROR_LOG_MESSAGE + ); + } + + /** + * Finds usages of pause action in action group files + * @param array $actionGroupXmlFiles + * @return array + */ + private function validatePauseActionUsageInActionGroups($actionGroupXmlFiles) + { + $actionGroupErrors = []; + foreach ($actionGroupXmlFiles as $filePath) { + $domDocument = new \DOMDocument(); + $domDocument->load($filePath); + $actionGroup = $domDocument->getElementsByTagName('actionGroup')->item(0); + $violatingStepKeys = $this->findViolatingPauseStepKeys($actionGroup); + $actionGroupErrors = array_merge($actionGroupErrors, $this->setErrorOutput($violatingStepKeys, $filePath)); + } + return $actionGroupErrors; + } + + /** + * Finds usages of pause action in test files + * @param array $testXmlFiles + * @return array + */ + private function validatePauseActionUsageInTests($testXmlFiles) + { + $testErrors = []; + foreach ($testXmlFiles as $filePath) { + $domDocument = new \DOMDocument(); + $domDocument->load($filePath); + $test = $domDocument->getElementsByTagName('test')->item(0); + $violatingStepKeys = $this->findViolatingPauseStepKeys($test); + $testErrors = array_merge($testErrors, $this->setErrorOutput($violatingStepKeys, $filePath)); + } + return $testErrors; + } + + /** + * Finds usages of pause action in suite files + * @param array $suiteXmlFiles + * @return array + */ + private function validatePauseActionUsageInSuites($suiteXmlFiles) + { + $suiteErrors = []; + foreach ($suiteXmlFiles as $filePath) { + $domDocument = new \DOMDocument(); + $domDocument->load($filePath); + $suite = $domDocument->getElementsByTagName('suite')->item(0); + $violatingStepKeys = $this->findViolatingPauseStepKeys($suite); + $suiteErrors = array_merge($suiteErrors, $this->setErrorOutput($violatingStepKeys, $filePath)); + } + return $suiteErrors; + } + + /** + * Finds violating pause action step keys + * @param \DomNode $entity + * @return array + */ + private function findViolatingPauseStepKeys($entity) + { + $violatingStepKeys = []; + $entityName = $entity->getAttribute('name'); + $references = $entity->getElementsByTagName('pause'); + + foreach ($references as $reference) { + $pauseStepKey = $reference->getAttribute('stepKey'); + $violatingStepKeys[$entityName][] = $pauseStepKey; + } + return $violatingStepKeys; + } + + /** + * 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 errors found." + * @return string + */ + public function getOutput() + { + return $this->output; + } + + /** + * Build and return error output for pause action usages + * + * @param array $violatingReferences + * @param SplFileInfo $path + * @return mixed + */ + private function setErrorOutput($violatingReferences, $path) + { + $testErrors = []; + + $filePath = StaticChecksList::getFilePath($path->getRealPath()); + + if (!empty($violatingReferences)) { + // Build error output + $errorOutput = "\nFile \"{$filePath}\""; + $errorOutput .= "\ncontains pause action(s):\n\t\t"; + foreach ($violatingReferences as $entityName => $stepKey) { + $errorOutput .= "\n\t {$entityName} has pause action at stepKey(s): " . implode(", ", $stepKey); + } + $testErrors[$filePath][] = $errorOutput; + } + return $testErrors; + } +} diff --git a/src/Magento/FunctionalTestingFramework/StaticCheck/StaticChecksList.php b/src/Magento/FunctionalTestingFramework/StaticCheck/StaticChecksList.php index ba2139276..07c52ce8c 100644 --- a/src/Magento/FunctionalTestingFramework/StaticCheck/StaticChecksList.php +++ b/src/Magento/FunctionalTestingFramework/StaticCheck/StaticChecksList.php @@ -17,6 +17,7 @@ class StaticChecksList implements StaticCheckListInterface { const DEPRECATED_ENTITY_USAGE_CHECK_NAME = 'deprecatedEntityUsage'; + const PAUSE_ACTION_USAGE_CHECK_NAME = 'pauseActionUsage'; const STATIC_RESULTS = 'tests' . DIRECTORY_SEPARATOR .'_output' . DIRECTORY_SEPARATOR . 'static-results'; /** @@ -45,7 +46,8 @@ public function __construct(array $checks = []) 'testDependencies' => new TestDependencyCheck(), 'actionGroupArguments' => new ActionGroupArgumentsCheck(), self::DEPRECATED_ENTITY_USAGE_CHECK_NAME => new DeprecatedEntityUsageCheck(), - 'annotations' => new AnnotationsCheck() + 'annotations' => new AnnotationsCheck(), + self::PAUSE_ACTION_USAGE_CHECK_NAME => new PauseActionUsageCheck() ] + $checks; // Static checks error files directory diff --git a/src/Magento/FunctionalTestingFramework/Util/Script/ScriptUtil.php b/src/Magento/FunctionalTestingFramework/Util/Script/ScriptUtil.php index 14f9c27e0..11afa5cc1 100644 --- a/src/Magento/FunctionalTestingFramework/Util/Script/ScriptUtil.php +++ b/src/Magento/FunctionalTestingFramework/Util/Script/ScriptUtil.php @@ -101,7 +101,7 @@ public function getModuleXmlFilesByScope($modulePaths, $scope) if (!realpath($modulePath . $scopePath)) { continue; } - $finder->files()->followLinks()->in($modulePath . $scopePath)->name("*.xml"); + $finder->files()->followLinks()->in($modulePath . $scopePath)->name("*.xml")->sortByName(); $found = true; } return $found ? $finder->files() : [];