diff --git a/dev/tests/verification/Resources/ActionsInDifferentModulesSuite.txt b/dev/tests/verification/Resources/ActionsInDifferentModulesSuite.txt new file mode 100644 index 000000000..d4e1b6eb3 --- /dev/null +++ b/dev/tests/verification/Resources/ActionsInDifferentModulesSuite.txt @@ -0,0 +1,218 @@ +webDriver = $this->getModule('\Magento\FunctionalTestingFramework\Module\MagentoWebDriver'); + $this->moduleContainer = $this->webDriver->getModuleContainer(); + // increment test count per execution + $this->currentTestRun++; + $this->executePreConditions(); + + if ($this->preconditionFailure != null) { + //if our preconditions fail, we need to mark all the tests as incomplete. + $e->getTest()->getMetadata()->setIncomplete("SUITE PRECONDITION FAILED:" . PHP_EOL . $this->preconditionFailure); + } + } + + private function executePreConditions() + { + if ($this->currentTestRun == 1) { + print sprintf(self::$HOOK_EXECUTION_INIT, "before"); + + try { + if ($this->webDriver->webDriver != null) { + $this->webDriver->_restart(); + } else { + $this->webDriver->_initializeSession(); + } + $cli = $this->getModuleForAction("magentoCLISecret")->magentoCLISecret($this->getModuleForAction("getSecret")->getSecret("magento/some/secret"), 60); // stepKey: cli + print($cli); // stepKey: cli + $create1Fields['someKey'] = "dataHere"; + PersistedObjectHandler::getInstance()->createEntity( + "create1", + "suite", + "SecretData", + [], + $create1Fields + ); + PersistedObjectHandler::getInstance()->createEntity( + "create2", + "suite", + "SecretData", + [] + ); + PersistedObjectHandler::getInstance()->createEntity( + "create3", + "suite", + "SecretData", + ["create1", "create2"] + ); + $this->getModuleForAction("fillSecretField")->fillSecretField("#fill", $this->getModuleForAction("getSecret")->getSecret("magento/some/secret") . "+" . $this->getModuleForAction("getSecret")->getSecret("magento/some/secret")); // stepKey: fillBefore + $this->getModuleForAction("click")->click(PersistedObjectHandler::getInstance()->retrieveEntityField('create2', 'key2', 'suite')); // stepKey: click + print("Entering Action Group [return1] ActionGroupReturningValueActionGroup"); + $grabProducts1Return1 = $this->getModuleForAction("grabMultiple")->grabMultiple("selector"); // stepKey: grabProducts1Return1 + $this->getModuleForAction("assertCount")->assertCount(1, $grabProducts1Return1); // stepKey: assertCountReturn1 + $return1 = $this->getModuleForAction("return")->return($grabProducts1Return1); // stepKey: returnProducts1Return1 + print("Exiting Action Group [return1] ActionGroupReturningValueActionGroup"); + } catch (\Exception $exception) { + $this->preconditionFailure = $exception->getMessage(); + } + + // reset configuration and close session + $this->webDriver->_resetConfig(); + $this->webDriver->webDriver->close(); + $this->webDriver->webDriver = null; + + print sprintf(self::$HOOK_EXECUTION_END, "before"); + } + } + + public function _after(\Codeception\Event\TestEvent $e) + { + $this->executePostConditions($e); + } + + private function executePostConditions(\Codeception\Event\TestEvent $e) + { + if ($this->currentTestRun == $this->testCount) { + print sprintf(self::$HOOK_EXECUTION_INIT, "after"); + + try { + // Find out if Test in Suite failed, will cause potential failures in suite after + $cest = $e->getTest(); + + //Access private TestResultObject to find stack and if there are any errors (as opposed to failures) + $testResultObject = call_user_func(\Closure::bind( + function () use ($cest) { + return $cest->getTestResultObject(); + }, + $cest + )); + $errors = $testResultObject->errors(); + + if (!empty($errors)) { + foreach ($errors as $error) { + if ($error->failedTest()->getTestMethod() == $cest->getName()) { + // Do not attempt to run _after if failure was in the _after block + // Try to run _after but catch exceptions to prevent them from overwriting original failure. + print("LAST TEST IN SUITE FAILED, TEST AFTER MAY NOT BE SUCCESSFUL\n"); + } + } + } + if ($this->webDriver->webDriver != null) { + $this->webDriver->_restart(); + } else { + $this->webDriver->_initializeSession(); + } + print("Entering Action Group [return2] ExtendedActionGroupReturningValueActionGroup"); + $grabProducts1Return2 = $this->getModuleForAction("grabMultiple")->grabMultiple("selector"); // stepKey: grabProducts1Return2 + $this->getModuleForAction("assertCount")->assertCount(1, $grabProducts1Return2); // stepKey: assertCountReturn2 + $return2 = $this->getModuleForAction("return")->return($grabProducts1Return2); // stepKey: returnProducts1Return2 + $grabProducts2Return2 = $this->getModuleForAction("grabMultiple")->grabMultiple("otherSelector"); // stepKey: grabProducts2Return2 + $this->getModuleForAction("assertCount")->assertCount(2, $grabProducts2Return2); // stepKey: assertSecondCountReturn2 + $return2 = $this->getModuleForAction("return")->return($grabProducts2Return2); // stepKey: returnProducts2Return2 + print("Exiting Action Group [return2] ExtendedActionGroupReturningValueActionGroup"); + PersistedObjectHandler::getInstance()->deleteEntity( + "create1", + "suite" + ); + PersistedObjectHandler::getInstance()->deleteEntity( + "create2", + "suite" + ); + PersistedObjectHandler::getInstance()->deleteEntity( + "create3", + "suite" + ); + $this->getModuleForAction("deleteEntityByUrl")->deleteEntityByUrl("deleteThis"); // stepKey: deleteThis + $this->getModuleForAction("fillSecretField")->fillSecretField("#fill", $this->getModuleForAction("getSecret")->getSecret("magento/some/secret")); // stepKey: fillAfter + $cli2 = $this->getModuleForAction("magentoCLISecret")->magentoCLISecret($this->getModuleForAction("getSecret")->getSecret("magento/some/secret") . "-some/data-" . $this->getModuleForAction("getSecret")->getSecret("magento/some/secret"), 60); // stepKey: cli2 + print($cli2); // stepKey: cli2 + } catch (\Exception $exception) { + print $exception->getMessage(); + } + + PersistedObjectHandler::getInstance()->clearSuiteObjects(); + + $this->closeSession($this->webDriver); + + print sprintf(self::$HOOK_EXECUTION_END, "after"); + } + } + + /** + * Close session method closes current session. + * If config 'close_all_sessions' is set to 'true' all sessions will be closed. + * + * return void + */ + private function closeSession(): void + { + $webDriverConfig = $this->webDriver->_getConfig(); + $this->webDriver->_closeSession(); + if (isset($webDriverConfig['close_all_sessions']) && $webDriverConfig['close_all_sessions'] === "true") { + $wdHost = sprintf( + '%s://%s:%s%s', + $webDriverConfig['protocol'], + $webDriverConfig['host'], + $webDriverConfig['port'], + $webDriverConfig['path'] + ); + $availableSessions = RemoteWebDriver::getAllSessions($wdHost); + foreach ($availableSessions as $session) { + $remoteWebDriver = RemoteWebDriver::createBySessionID($session['id'], $wdHost); + $remoteWebDriver->quit(); + } + } + } + + /** + * Return the module for an action. + * + * @param string $action + * @return Module + * @throws \Exception + */ + private function getModuleForAction($action) + { + $module = $this->moduleContainer->moduleForAction($action); + if ($module === null) { + throw new TestFrameworkException('Invalid action "' . $action . '"' . PHP_EOL); + } + return $module; + } +} diff --git a/dev/tests/verification/Resources/functionalSuiteHooks.txt b/dev/tests/verification/Resources/functionalSuiteHooks.txt index 041b088b1..3156a59dd 100644 --- a/dev/tests/verification/Resources/functionalSuiteHooks.txt +++ b/dev/tests/verification/Resources/functionalSuiteHooks.txt @@ -6,6 +6,11 @@ use Facebook\WebDriver\Remote\RemoteWebDriver; use Magento\FunctionalTestingFramework\DataGenerator\Handlers\PersistedObjectHandler; use Magento\FunctionalTestingFramework\DataGenerator\Handlers\CredentialStore; use Magento\FunctionalTestingFramework\Module\MagentoWebDriver; +use Magento\FunctionalTestingFramework\Module\MagentoAssert; +use Magento\FunctionalTestingFramework\Module\MagentoActionProxies; +use Magento\FunctionalTestingFramework\Exceptions\TestFrameworkException; +use Codeception\Lib\ModuleContainer; +use Codeception\Module; /** * Group class is Codeception Extension which is allowed to handle to all internal events. @@ -24,9 +29,15 @@ class functionalSuiteHooks extends \Codeception\GroupObject private $currentTestRun = 0; private static $HOOK_EXECUTION_INIT = "\n/******** Beginning execution of functionalSuiteHooks suite %s block ********/\n"; private static $HOOK_EXECUTION_END = "\n/******** Execution of functionalSuiteHooks suite %s block complete ********/\n"; + /** @var MagentoWebDriver */ + private $webDriver; + /** @var ModuleContainer */ + private $moduleContainer; public function _before(\Codeception\Event\TestEvent $e) { + $this->webDriver = $this->getModule('\Magento\FunctionalTestingFramework\Module\MagentoWebDriver'); + $this->moduleContainer = $this->webDriver->getModuleContainer(); // increment test count per execution $this->currentTestRun++; $this->executePreConditions(); @@ -37,22 +48,18 @@ class functionalSuiteHooks extends \Codeception\GroupObject } } - private function executePreConditions() { if ($this->currentTestRun == 1) { print sprintf(self::$HOOK_EXECUTION_INIT, "before"); - /** @var MagentoWebDriver $webDriver */ - $webDriver = $this->getModule('\Magento\FunctionalTestingFramework\Module\MagentoWebDriver'); - try { - if ($webDriver->webDriver != null) { - $webDriver->_restart(); + if ($this->webDriver->webDriver != null) { + $this->webDriver->_restart(); } else { - $webDriver->_initializeSession(); + $this->webDriver->_initializeSession(); } - $webDriver->amOnPage("some.url"); // stepKey: before + $this->getModuleForAction("amOnPage")->amOnPage("some.url"); // stepKey: before $createOneFields['someKey'] = "dataHere"; PersistedObjectHandler::getInstance()->createEntity( "createOne", @@ -79,18 +86,18 @@ class functionalSuiteHooks extends \Codeception\GroupObject "createEntityFour", ["createEntityTwo", "createEntityThree"] ); - $webDriver->click(PersistedObjectHandler::getInstance()->retrieveEntityField('createTwo', 'data', 'suite')); // stepKey: clickWithData + $this->getModuleForAction("click")->click(PersistedObjectHandler::getInstance()->retrieveEntityField('createTwo', 'data', 'suite')); // stepKey: clickWithData print("Entering Action Group [AC] actionGroupWithTwoArguments"); - $webDriver->see("John", msq("uniqueData") . "John"); // stepKey: seeFirstNameAC + $this->getModuleForAction("see")->see("John", msq("uniqueData") . "John"); // stepKey: seeFirstNameAC print("Exiting Action Group [AC] actionGroupWithTwoArguments"); } catch (\Exception $exception) { $this->preconditionFailure = $exception->getMessage(); } // reset configuration and close session - $webDriver->_resetConfig(); - $webDriver->webDriver->close(); - $webDriver->webDriver = null; + $this->webDriver->_resetConfig(); + $this->webDriver->webDriver->close(); + $this->webDriver->webDriver = null; print sprintf(self::$HOOK_EXECUTION_END, "before"); } @@ -101,15 +108,11 @@ class functionalSuiteHooks extends \Codeception\GroupObject $this->executePostConditions($e); } - private function executePostConditions(\Codeception\Event\TestEvent $e) { if ($this->currentTestRun == $this->testCount) { print sprintf(self::$HOOK_EXECUTION_INIT, "after"); - /** @var MagentoWebDriver $webDriver */ - $webDriver = $this->getModule('\Magento\FunctionalTestingFramework\Module\MagentoWebDriver'); - try { // Find out if Test in Suite failed, will cause potential failures in suite after $cest = $e->getTest(); @@ -132,15 +135,15 @@ class functionalSuiteHooks extends \Codeception\GroupObject } } } - if ($webDriver->webDriver != null) { - $webDriver->_restart(); + if ($this->webDriver->webDriver != null) { + $this->webDriver->_restart(); } else { - $webDriver->_initializeSession(); + $this->webDriver->_initializeSession(); } - $webDriver->amOnPage("some.url"); // stepKey: after - $webDriver->deleteEntityByUrl("deleteThis"); // stepKey: delete + $this->getModuleForAction("amOnPage")->amOnPage("some.url"); // stepKey: after + $this->getModuleForAction("deleteEntityByUrl")->deleteEntityByUrl("deleteThis"); // stepKey: delete print("Entering Action Group [AC] actionGroupWithTwoArguments"); - $webDriver->see("John", msq("uniqueData") . "John"); // stepKey: seeFirstNameAC + $this->getModuleForAction("see")->see("John", msq("uniqueData") . "John"); // stepKey: seeFirstNameAC print("Exiting Action Group [AC] actionGroupWithTwoArguments"); } catch (\Exception $exception) { print $exception->getMessage(); @@ -148,7 +151,7 @@ class functionalSuiteHooks extends \Codeception\GroupObject PersistedObjectHandler::getInstance()->clearSuiteObjects(); - $this->closeSession($webDriver); + $this->closeSession($this->webDriver); print sprintf(self::$HOOK_EXECUTION_END, "after"); } @@ -158,13 +161,12 @@ class functionalSuiteHooks extends \Codeception\GroupObject * Close session method closes current session. * If config 'close_all_sessions' is set to 'true' all sessions will be closed. * - * @param MagentoWebDriver $webDriver * return void */ - private function closeSession(MagentoWebDriver $webDriver): void + private function closeSession(): void { - $webDriverConfig = $webDriver->_getConfig(); - $webDriver->_closeSession(); + $webDriverConfig = $this->webDriver->_getConfig(); + $this->webDriver->_closeSession(); if (isset($webDriverConfig['close_all_sessions']) && $webDriverConfig['close_all_sessions'] === "true") { $wdHost = sprintf( '%s://%s:%s%s', @@ -180,4 +182,20 @@ class functionalSuiteHooks extends \Codeception\GroupObject } } } + + /** + * Return the module for an action. + * + * @param string $action + * @return Module + * @throws \Exception + */ + private function getModuleForAction($action) + { + $module = $this->moduleContainer->moduleForAction($action); + if ($module === null) { + throw new TestFrameworkException('Invalid action "' . $action . '"' . PHP_EOL); + } + return $module; + } } diff --git a/dev/tests/verification/Resources/functionalSuiteWithComments.txt b/dev/tests/verification/Resources/functionalSuiteWithComments.txt index 6c646dd18..02b50f092 100644 --- a/dev/tests/verification/Resources/functionalSuiteWithComments.txt +++ b/dev/tests/verification/Resources/functionalSuiteWithComments.txt @@ -6,6 +6,11 @@ use Facebook\WebDriver\Remote\RemoteWebDriver; use Magento\FunctionalTestingFramework\DataGenerator\Handlers\PersistedObjectHandler; use Magento\FunctionalTestingFramework\DataGenerator\Handlers\CredentialStore; use Magento\FunctionalTestingFramework\Module\MagentoWebDriver; +use Magento\FunctionalTestingFramework\Module\MagentoAssert; +use Magento\FunctionalTestingFramework\Module\MagentoActionProxies; +use Magento\FunctionalTestingFramework\Exceptions\TestFrameworkException; +use Codeception\Lib\ModuleContainer; +use Codeception\Module; /** * Group class is Codeception Extension which is allowed to handle to all internal events. @@ -24,9 +29,15 @@ class functionalSuiteWithComments extends \Codeception\GroupObject private $currentTestRun = 0; private static $HOOK_EXECUTION_INIT = "\n/******** Beginning execution of functionalSuiteWithComments suite %s block ********/\n"; private static $HOOK_EXECUTION_END = "\n/******** Execution of functionalSuiteWithComments suite %s block complete ********/\n"; + /** @var MagentoWebDriver */ + private $webDriver; + /** @var ModuleContainer */ + private $moduleContainer; public function _before(\Codeception\Event\TestEvent $e) { + $this->webDriver = $this->getModule('\Magento\FunctionalTestingFramework\Module\MagentoWebDriver'); + $this->moduleContainer = $this->webDriver->getModuleContainer(); // increment test count per execution $this->currentTestRun++; $this->executePreConditions(); @@ -37,23 +48,19 @@ class functionalSuiteWithComments extends \Codeception\GroupObject } } - private function executePreConditions() { if ($this->currentTestRun == 1) { print sprintf(self::$HOOK_EXECUTION_INIT, "before"); - /** @var MagentoWebDriver $webDriver */ - $webDriver = $this->getModule('\Magento\FunctionalTestingFramework\Module\MagentoWebDriver'); - try { - if ($webDriver->webDriver != null) { - $webDriver->_restart(); + if ($this->webDriver->webDriver != null) { + $this->webDriver->_restart(); } else { - $webDriver->_initializeSession(); + $this->webDriver->_initializeSession(); } print("Comment in Before"); - $webDriver->amOnPage("some.url"); // stepKey: before + $this->getModuleForAction("amOnPage")->amOnPage("some.url"); // stepKey: before $createFields['someKey'] = "dataHere"; PersistedObjectHandler::getInstance()->createEntity( "create", @@ -63,18 +70,18 @@ class functionalSuiteWithComments extends \Codeception\GroupObject $createFields ); print(""); - $webDriver->click(PersistedObjectHandler::getInstance()->retrieveEntityField('create', 'data', 'suite')); // stepKey: clickWithData + $this->getModuleForAction("click")->click(PersistedObjectHandler::getInstance()->retrieveEntityField('create', 'data', 'suite')); // stepKey: clickWithData print("Entering Action Group [AC] actionGroupWithTwoArguments"); - $webDriver->see("John", msq("uniqueData") . "John"); // stepKey: seeFirstNameAC + $this->getModuleForAction("see")->see("John", msq("uniqueData") . "John"); // stepKey: seeFirstNameAC print("Exiting Action Group [AC] actionGroupWithTwoArguments"); } catch (\Exception $exception) { $this->preconditionFailure = $exception->getMessage(); } // reset configuration and close session - $webDriver->_resetConfig(); - $webDriver->webDriver->close(); - $webDriver->webDriver = null; + $this->webDriver->_resetConfig(); + $this->webDriver->webDriver->close(); + $this->webDriver->webDriver = null; print sprintf(self::$HOOK_EXECUTION_END, "before"); } @@ -85,15 +92,11 @@ class functionalSuiteWithComments extends \Codeception\GroupObject $this->executePostConditions($e); } - private function executePostConditions(\Codeception\Event\TestEvent $e) { if ($this->currentTestRun == $this->testCount) { print sprintf(self::$HOOK_EXECUTION_INIT, "after"); - /** @var MagentoWebDriver $webDriver */ - $webDriver = $this->getModule('\Magento\FunctionalTestingFramework\Module\MagentoWebDriver'); - try { // Find out if Test in Suite failed, will cause potential failures in suite after $cest = $e->getTest(); @@ -116,10 +119,10 @@ class functionalSuiteWithComments extends \Codeception\GroupObject } } } - if ($webDriver->webDriver != null) { - $webDriver->_restart(); + if ($this->webDriver->webDriver != null) { + $this->webDriver->_restart(); } else { - $webDriver->_initializeSession(); + $this->webDriver->_initializeSession(); } print("afterBlock"); } catch (\Exception $exception) { @@ -128,7 +131,7 @@ class functionalSuiteWithComments extends \Codeception\GroupObject PersistedObjectHandler::getInstance()->clearSuiteObjects(); - $this->closeSession($webDriver); + $this->closeSession($this->webDriver); print sprintf(self::$HOOK_EXECUTION_END, "after"); } @@ -138,13 +141,12 @@ class functionalSuiteWithComments extends \Codeception\GroupObject * Close session method closes current session. * If config 'close_all_sessions' is set to 'true' all sessions will be closed. * - * @param MagentoWebDriver $webDriver * return void */ - private function closeSession(MagentoWebDriver $webDriver): void + private function closeSession(): void { - $webDriverConfig = $webDriver->_getConfig(); - $webDriver->_closeSession(); + $webDriverConfig = $this->webDriver->_getConfig(); + $this->webDriver->_closeSession(); if (isset($webDriverConfig['close_all_sessions']) && $webDriverConfig['close_all_sessions'] === "true") { $wdHost = sprintf( '%s://%s:%s%s', @@ -160,4 +162,20 @@ class functionalSuiteWithComments extends \Codeception\GroupObject } } } + + /** + * Return the module for an action. + * + * @param string $action + * @return Module + * @throws \Exception + */ + private function getModuleForAction($action) + { + $module = $this->moduleContainer->moduleForAction($action); + if ($module === null) { + throw new TestFrameworkException('Invalid action "' . $action . '"' . PHP_EOL); + } + return $module; + } } diff --git a/dev/tests/verification/TestModule/Data/PersistedReplacementData.xml b/dev/tests/verification/TestModule/Data/PersistedReplacementData.xml index 514b5eec6..7213fa6b9 100644 --- a/dev/tests/verification/TestModule/Data/PersistedReplacementData.xml +++ b/dev/tests/verification/TestModule/Data/PersistedReplacementData.xml @@ -22,4 +22,8 @@ Dane unmerged + + some/data + {{_CREDS.magento/some/secret}} + diff --git a/dev/tests/verification/TestModule/Suite/ActionsInDifferentModulesSuite.xml b/dev/tests/verification/TestModule/Suite/ActionsInDifferentModulesSuite.xml new file mode 100644 index 000000000..e3553308d --- /dev/null +++ b/dev/tests/verification/TestModule/Suite/ActionsInDifferentModulesSuite.xml @@ -0,0 +1,43 @@ + + + + + + + + + + + + dataHere + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/dev/tests/verification/TestModule/Test/SampleSuiteTest/IncludeActionsInDifferentModulesTest.xml b/dev/tests/verification/TestModule/Test/SampleSuiteTest/IncludeActionsInDifferentModulesTest.xml new file mode 100644 index 000000000..1be662bbe --- /dev/null +++ b/dev/tests/verification/TestModule/Test/SampleSuiteTest/IncludeActionsInDifferentModulesTest.xml @@ -0,0 +1,20 @@ + + + + + + dataHere + + + + + + + + diff --git a/dev/tests/verification/Tests/SuiteGenerationTest.php b/dev/tests/verification/Tests/SuiteGenerationTest.php index 4a8371692..f37283871 100644 --- a/dev/tests/verification/Tests/SuiteGenerationTest.php +++ b/dev/tests/verification/Tests/SuiteGenerationTest.php @@ -388,6 +388,65 @@ public function testSuiteCommentsGeneration() ); } + /** + * Test suite generation with actions from different modules + */ + public function testSuiteGenerationActionsInDifferentModules() + { + $groupName = 'ActionsInDifferentModulesSuite'; + + $expectedContents = [ + 'IncludeActionsInDifferentModulesTestCest.php' + ]; + + // Generate the Suite + SuiteGenerator::getInstance()->generateSuite($groupName); + + // Validate log message and add group name for later deletion + TestLoggingUtil::getInstance()->validateMockLogStatement( + 'info', + "suite generated", + ['suite' => $groupName, 'relative_path' => "_generated" . DIRECTORY_SEPARATOR . $groupName] + ); + self::$TEST_GROUPS[] = $groupName; + + // Validate Yaml file updated + $yml = Yaml::parse(file_get_contents(self::CONFIG_YML_FILE)); + $this->assertArrayHasKey($groupName, $yml['groups']); + + $suiteResultBaseDir = self::GENERATE_RESULT_DIR . + DIRECTORY_SEPARATOR . + $groupName . + DIRECTORY_SEPARATOR; + + // Validate tests have been generated + $dirContents = array_diff(scandir($suiteResultBaseDir), ['..', '.']); + + foreach ($expectedContents as $expectedFile) { + $this->assertTrue(in_array($expectedFile, $dirContents)); + } + + //assert group file created and contains correct contents + $groupFile = PROJECT_ROOT . + DIRECTORY_SEPARATOR . + "src" . + DIRECTORY_SEPARATOR . + "Magento" . + DIRECTORY_SEPARATOR . + "FunctionalTestingFramework" . + DIRECTORY_SEPARATOR . + "Group" . + DIRECTORY_SEPARATOR . + $groupName . + ".php"; + + $this->assertTrue(file_exists($groupFile)); + $this->assertFileEquals( + self::RESOURCES_PATH . DIRECTORY_SEPARATOR . $groupName . ".txt", + $groupFile + ); + } + /** * revert any changes made to config.yml * remove _generated directory diff --git a/src/Magento/FunctionalTestingFramework/Module/MagentoActionProxies.php b/src/Magento/FunctionalTestingFramework/Module/MagentoActionProxies.php index 5a3b2360b..55b19a7c1 100644 --- a/src/Magento/FunctionalTestingFramework/Module/MagentoActionProxies.php +++ b/src/Magento/FunctionalTestingFramework/Module/MagentoActionProxies.php @@ -20,5 +20,122 @@ */ class MagentoActionProxies extends CodeceptionModule { - // TODO: placeholder for proxy functions currently in MagentoWebDriver (MQE-1904) + /** + * Create an entity + * + * @param string $key StepKey of the createData action. + * @param string $scope + * @param string $entity Name of xml entity to create. + * @param array $dependentObjectKeys StepKeys of other createData actions that are required. + * @param array $overrideFields Array of FieldName => Value of override fields. + * @param string $storeCode + * @return void + */ + public function createEntity( + $key, + $scope, + $entity, + $dependentObjectKeys = [], + $overrideFields = [], + $storeCode = '' + ) { + PersistedObjectHandler::getInstance()->createEntity( + $key, + $scope, + $entity, + $dependentObjectKeys, + $overrideFields, + $storeCode + ); + } + + /** + * Retrieves and updates a previously created entity + * + * @param string $key StepKey of the createData action. + * @param string $scope + * @param string $updateEntity Name of the static XML data to update the entity with. + * @param array $dependentObjectKeys StepKeys of other createData actions that are required. + * @return void + */ + public function updateEntity($key, $scope, $updateEntity, $dependentObjectKeys = []) + { + PersistedObjectHandler::getInstance()->updateEntity( + $key, + $scope, + $updateEntity, + $dependentObjectKeys + ); + } + + /** + * Performs GET on given entity and stores entity for use + * + * @param string $key StepKey of getData action. + * @param string $scope + * @param string $entity Name of XML static data to use. + * @param array $dependentObjectKeys StepKeys of other createData actions that are required. + * @param string $storeCode + * @param integer $index + * @return void + */ + public function getEntity($key, $scope, $entity, $dependentObjectKeys = [], $storeCode = '', $index = null) + { + PersistedObjectHandler::getInstance()->getEntity( + $key, + $scope, + $entity, + $dependentObjectKeys, + $storeCode, + $index + ); + } + + /** + * Retrieves and deletes a previously created entity + * + * @param string $key StepKey of the createData action. + * @param string $scope + * @return void + */ + public function deleteEntity($key, $scope) + { + PersistedObjectHandler::getInstance()->deleteEntity($key, $scope); + } + + /** + * Retrieves a field from an entity, according to key and scope given + * + * @param string $stepKey + * @param string $field + * @param string $scope + * @return string + */ + public function retrieveEntityField($stepKey, $field, $scope) + { + return PersistedObjectHandler::getInstance()->retrieveEntityField($stepKey, $field, $scope); + } + + /** + * Get encrypted value by key + * + * @param string $key + * @return string|null + * @throws TestFrameworkException + */ + public function getSecret($key) + { + return CredentialStore::getInstance()->getSecret($key); + } + + /** + * Returns a value to origin of the action + * + * @param mixed $value + * @return mixed + */ + public function return($value) + { + return $value; + } } diff --git a/src/Magento/FunctionalTestingFramework/Module/MagentoWebDriver.php b/src/Magento/FunctionalTestingFramework/Module/MagentoWebDriver.php index 944735677..ef57aa3f1 100644 --- a/src/Magento/FunctionalTestingFramework/Module/MagentoWebDriver.php +++ b/src/Magento/FunctionalTestingFramework/Module/MagentoWebDriver.php @@ -15,6 +15,7 @@ use Codeception\Exception\ModuleConfigException; use Codeception\Exception\ModuleException; use Codeception\Util\Uri; +use Codeception\Lib\ModuleContainer; use Magento\FunctionalTestingFramework\DataTransport\WebApiExecutor; use Magento\FunctionalTestingFramework\DataTransport\Auth\WebApiAuth; use Magento\FunctionalTestingFramework\DataTransport\Auth\Tfa\OTP; @@ -180,6 +181,16 @@ public function _after(TestInterface $test) // DO NOT RESET SESSIONS } + /** + * Return ModuleContainer + * + * @return ModuleContainer + */ + public function getModuleContainer() + { + return $this->moduleContainer; + } + /** * Returns URL of a host. * @@ -956,120 +967,6 @@ public function getOTP() return OTP::getOTP(); } - /** - * Create an entity - * TODO: move this function to MagentoActionProxies after MQE-1904 - * - * @param string $key StepKey of the createData action. - * @param string $scope - * @param string $entity Name of xml entity to create. - * @param array $dependentObjectKeys StepKeys of other createData actions that are required. - * @param array $overrideFields Array of FieldName => Value of override fields. - * @param string $storeCode - * @return void - */ - public function createEntity( - $key, - $scope, - $entity, - $dependentObjectKeys = [], - $overrideFields = [], - $storeCode = '' - ) { - PersistedObjectHandler::getInstance()->createEntity( - $key, - $scope, - $entity, - $dependentObjectKeys, - $overrideFields, - $storeCode - ); - } - - /** - * Retrieves and updates a previously created entity - * TODO: move this function to MagentoActionProxies after MQE-1904 - * - * @param string $key StepKey of the createData action. - * @param string $scope - * @param string $updateEntity Name of the static XML data to update the entity with. - * @param array $dependentObjectKeys StepKeys of other createData actions that are required. - * @return void - */ - public function updateEntity($key, $scope, $updateEntity, $dependentObjectKeys = []) - { - PersistedObjectHandler::getInstance()->updateEntity( - $key, - $scope, - $updateEntity, - $dependentObjectKeys - ); - } - - /** - * Performs GET on given entity and stores entity for use - * TODO: move this function to MagentoActionProxies after MQE-1904 - * - * @param string $key StepKey of getData action. - * @param string $scope - * @param string $entity Name of XML static data to use. - * @param array $dependentObjectKeys StepKeys of other createData actions that are required. - * @param string $storeCode - * @param integer $index - * @return void - */ - public function getEntity($key, $scope, $entity, $dependentObjectKeys = [], $storeCode = '', $index = null) - { - PersistedObjectHandler::getInstance()->getEntity( - $key, - $scope, - $entity, - $dependentObjectKeys, - $storeCode, - $index - ); - } - - /** - * Retrieves and deletes a previously created entity - * TODO: move this function to MagentoActionProxies after MQE-1904 - * - * @param string $key StepKey of the createData action. - * @param string $scope - * @return void - */ - public function deleteEntity($key, $scope) - { - PersistedObjectHandler::getInstance()->deleteEntity($key, $scope); - } - - /** - * Retrieves a field from an entity, according to key and scope given - * TODO: move this function to MagentoActionProxies after MQE-1904 - * - * @param string $stepKey - * @param string $field - * @param string $scope - * @return string - */ - public function retrieveEntityField($stepKey, $field, $scope) - { - return PersistedObjectHandler::getInstance()->retrieveEntityField($stepKey, $field, $scope); - } - - /** - * Get encrypted value by key - * TODO: move this function to MagentoActionProxies after MQE-1904 - * - * @param string $key - * @return string|null - * @throws TestFrameworkException - */ - public function getSecret($key) - { - return CredentialStore::getInstance()->getSecret($key); - } - /** * Waits proper amount of time to perform Cron execution * @@ -1122,16 +1019,4 @@ public function switchToIFrame($locator = null) $this->webDriver->switchTo()->frame($els[0]); } } - - /** - * Returns a value to origin of the action. - * TODO: move this function to MagentoActionProxies after MQE-1904 - * - * @param mixed $value - * @return mixed - */ - public function return($value) - { - return $value; - } } diff --git a/src/Magento/FunctionalTestingFramework/Suite/Generators/GroupClassGenerator.php b/src/Magento/FunctionalTestingFramework/Suite/Generators/GroupClassGenerator.php index bb0157152..f90a25322 100644 --- a/src/Magento/FunctionalTestingFramework/Suite/Generators/GroupClassGenerator.php +++ b/src/Magento/FunctionalTestingFramework/Suite/Generators/GroupClassGenerator.php @@ -6,6 +6,7 @@ namespace Magento\FunctionalTestingFramework\Suite\Generators; +use Magento\FunctionalTestingFramework\Exceptions\TestFrameworkException; use Magento\FunctionalTestingFramework\Exceptions\TestReferenceException; use Magento\FunctionalTestingFramework\Suite\Objects\SuiteObject; use Magento\FunctionalTestingFramework\Test\Objects\ActionObject; @@ -32,6 +33,10 @@ class GroupClassGenerator 'comment' => 'print' ]; const GROUP_DIR_NAME = 'Group'; + const FUNCTION_PLACEHOLDER = 'PLACEHOLDER'; + const FUNCTION_START = 'this->getModuleForAction("'; + const FUNCTION_END = '")'; + const FUNCTION_REPLACE_REGEX = '/(PLACEHOLDER->([^\(]+))\(/'; /** * Mustache_Engine instance for template loading @@ -142,7 +147,7 @@ private function buildHookMustacheArray($hookObj) //deleteData contains either url or createDataKey, if it contains the former it needs special formatting if ($action->getType() !== "createData" && !array_key_exists(TestGenerator::REQUIRED_ENTITY_REFERENCE, $action->getCustomActionAttributes())) { - $actions = $this->buildWebDriverActionsMustacheArray($action, $actions); + $actions = $this->buildModuleActionsMustacheArray($action, $actions); continue; } @@ -165,7 +170,7 @@ private function buildHookMustacheArray($hookObj) } /** - * Takes an action object and array of generated action steps. Converst the action object into generated php and + * Takes an action object and array of generated action steps. Convert the action object into generated php and * appends the entry to the given array. The result is returned by the function. * * @param ActionObject $action @@ -173,14 +178,22 @@ private function buildHookMustacheArray($hookObj) * @return array * @throws TestReferenceException */ - private function buildWebDriverActionsMustacheArray($action, $actionEntries) + private function buildModuleActionsMustacheArray($action, $actionEntries) { - $step = TestGenerator::getInstance()->generateStepsPhp([$action], TestGenerator::SUITE_SCOPE, 'webDriver'); + $step = TestGenerator::getInstance()->generateStepsPhp( + [$action], + TestGenerator::SUITE_SCOPE, + self::FUNCTION_PLACEHOLDER + ); $rawPhp = str_replace(["\t"], "", $step); $multipleCommands = explode(PHP_EOL, $rawPhp, -1); $multipleCommands = array_filter($multipleCommands); foreach ($multipleCommands as $command) { - $actionEntries = $this->replaceReservedTesterFunctions($command . PHP_EOL, $actionEntries, 'webDriver'); + $actionEntries = $this->replaceReservedTesterFunctions( + $command . PHP_EOL, + $actionEntries, + self::FUNCTION_PLACEHOLDER + ); } return $actionEntries; @@ -204,7 +217,17 @@ private function replaceReservedTesterFunctions($formattedStep, $actionEntries, $resultingStep = str_replace($testActionCall, $replacement, $formattedStep); $actionEntries[] = ['action' => $resultingStep]; } else { - $actionEntries[] = ['action' => $formattedStep]; + $placeholder = self::FUNCTION_PLACEHOLDER; + $begin = self::FUNCTION_START; + $end = self::FUNCTION_END; + $resultingStep = preg_replace_callback( + self::FUNCTION_REPLACE_REGEX, + function ($matches) use ($placeholder, $begin, $end) { + return str_replace($placeholder, $begin . $matches[2] . $end, $matches[1]) . '('; + }, + $formattedStep + ); + $actionEntries[] = ['action' => $resultingStep]; } } diff --git a/src/Magento/FunctionalTestingFramework/Suite/views/SuiteClass.mustache b/src/Magento/FunctionalTestingFramework/Suite/views/SuiteClass.mustache index a9fdcd6df..dc2fde915 100644 --- a/src/Magento/FunctionalTestingFramework/Suite/views/SuiteClass.mustache +++ b/src/Magento/FunctionalTestingFramework/Suite/views/SuiteClass.mustache @@ -6,6 +6,11 @@ use Facebook\WebDriver\Remote\RemoteWebDriver; use Magento\FunctionalTestingFramework\DataGenerator\Handlers\PersistedObjectHandler; use Magento\FunctionalTestingFramework\DataGenerator\Handlers\CredentialStore; use Magento\FunctionalTestingFramework\Module\MagentoWebDriver; +use Magento\FunctionalTestingFramework\Module\MagentoAssert; +use Magento\FunctionalTestingFramework\Module\MagentoActionProxies; +use Magento\FunctionalTestingFramework\Exceptions\TestFrameworkException; +use Codeception\Lib\ModuleContainer; +use Codeception\Module; /** * Group class is Codeception Extension which is allowed to handle to all internal events. @@ -30,10 +35,16 @@ class {{suiteName}} extends \Codeception\GroupObject {{/helpers}} private static $HOOK_EXECUTION_INIT = "\n/******** Beginning execution of {{suiteName}} suite %s block ********/\n"; private static $HOOK_EXECUTION_END = "\n/******** Execution of {{suiteName}} suite %s block complete ********/\n"; + /** @var MagentoWebDriver */ + private $webDriver; + /** @var ModuleContainer */ + private $moduleContainer; {{#before}} public function _before(\Codeception\Event\TestEvent $e) { + $this->webDriver = $this->getModule('\Magento\FunctionalTestingFramework\Module\MagentoWebDriver'); + $this->moduleContainer = $this->webDriver->getModuleContainer(); {{#helpers}} /** @var \Magento\FunctionalTestingFramework\Helper\HelperContainer $helperContainer */ $this->helperContainer = $this->getModule('\Magento\FunctionalTestingFramework\Helper\HelperContainer'); @@ -51,15 +62,11 @@ class {{suiteName}} extends \Codeception\GroupObject } } - private function executePreConditions() { if ($this->currentTestRun == 1) { print sprintf(self::$HOOK_EXECUTION_INIT, "before"); - /** @var MagentoWebDriver $webDriver */ - $webDriver = $this->getModule('\Magento\FunctionalTestingFramework\Module\MagentoWebDriver'); - try { {{> testActions}} } catch (\Exception $exception) { @@ -67,9 +74,9 @@ class {{suiteName}} extends \Codeception\GroupObject } // reset configuration and close session - $webDriver->_resetConfig(); - $webDriver->webDriver->close(); - $webDriver->webDriver = null; + $this->webDriver->_resetConfig(); + $this->webDriver->webDriver->close(); + $this->webDriver->webDriver = null; print sprintf(self::$HOOK_EXECUTION_END, "before"); } @@ -82,15 +89,11 @@ class {{suiteName}} extends \Codeception\GroupObject $this->executePostConditions($e); } - private function executePostConditions(\Codeception\Event\TestEvent $e) { if ($this->currentTestRun == $this->testCount) { print sprintf(self::$HOOK_EXECUTION_INIT, "after"); - /** @var MagentoWebDriver $webDriver */ - $webDriver = $this->getModule('\Magento\FunctionalTestingFramework\Module\MagentoWebDriver'); - try { // Find out if Test in Suite failed, will cause potential failures in suite after $cest = $e->getTest(); @@ -120,7 +123,7 @@ class {{suiteName}} extends \Codeception\GroupObject PersistedObjectHandler::getInstance()->clearSuiteObjects(); - $this->closeSession($webDriver); + $this->closeSession($this->webDriver); print sprintf(self::$HOOK_EXECUTION_END, "after"); } @@ -131,13 +134,12 @@ class {{suiteName}} extends \Codeception\GroupObject * Close session method closes current session. * If config 'close_all_sessions' is set to 'true' all sessions will be closed. * - * @param MagentoWebDriver $webDriver * return void */ - private function closeSession(MagentoWebDriver $webDriver): void + private function closeSession(): void { - $webDriverConfig = $webDriver->_getConfig(); - $webDriver->_closeSession(); + $webDriverConfig = $this->webDriver->_getConfig(); + $this->webDriver->_closeSession(); if (isset($webDriverConfig['close_all_sessions']) && $webDriverConfig['close_all_sessions'] === "true") { $wdHost = sprintf( '%s://%s:%s%s', @@ -153,4 +155,20 @@ class {{suiteName}} extends \Codeception\GroupObject } } } + + /** + * Return the module for an action. + * + * @param string $action + * @return Module + * @throws \Exception + */ + private function getModuleForAction($action) + { + $module = $this->moduleContainer->moduleForAction($action); + if ($module === null) { + throw new TestFrameworkException('Invalid action "' . $action . '"' . PHP_EOL); + } + return $module; + } } diff --git a/src/Magento/FunctionalTestingFramework/Suite/views/partials/testActions.mustache b/src/Magento/FunctionalTestingFramework/Suite/views/partials/testActions.mustache index eaa0959cb..c07958778 100644 --- a/src/Magento/FunctionalTestingFramework/Suite/views/partials/testActions.mustache +++ b/src/Magento/FunctionalTestingFramework/Suite/views/partials/testActions.mustache @@ -1,9 +1,9 @@ {{#actions}} {{#webDriverInit}} -if ($webDriver->webDriver != null) { - $webDriver->_restart(); +if ($this->webDriver->webDriver != null) { + $this->webDriver->_restart(); } else { - $webDriver->_initializeSession(); + $this->webDriver->_initializeSession(); } {{/webDriverInit}} {{#action}}