Skip to content

Commit da3465c

Browse files
authored
Merge pull request #779 from magento/MQE-2158
MQE-2158: support using actions from multiple modules in Suites
2 parents 6529865 + 9b20fff commit da3465c

File tree

12 files changed

+628
-205
lines changed

12 files changed

+628
-205
lines changed
Lines changed: 218 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,218 @@
1+
<?php
2+
3+
namespace Group;
4+
5+
use Facebook\WebDriver\Remote\RemoteWebDriver;
6+
use Magento\FunctionalTestingFramework\DataGenerator\Handlers\PersistedObjectHandler;
7+
use Magento\FunctionalTestingFramework\DataGenerator\Handlers\CredentialStore;
8+
use Magento\FunctionalTestingFramework\Module\MagentoWebDriver;
9+
use Magento\FunctionalTestingFramework\Module\MagentoAssert;
10+
use Magento\FunctionalTestingFramework\Module\MagentoActionProxies;
11+
use Magento\FunctionalTestingFramework\Exceptions\TestFrameworkException;
12+
use Codeception\Lib\ModuleContainer;
13+
use Codeception\Module;
14+
15+
/**
16+
* Group class is Codeception Extension which is allowed to handle to all internal events.
17+
* This class itself can be used to listen events for test execution of one particular group.
18+
* It may be especially useful to create fixtures data, prepare server, etc.
19+
*
20+
* INSTALLATION:
21+
*
22+
* To use this group extension, include it to "extensions" option of global Codeception config.
23+
*/
24+
class ActionsInDifferentModulesSuite extends \Codeception\GroupObject
25+
{
26+
public static $group = 'ActionsInDifferentModulesSuite';
27+
private $testCount = 1;
28+
private $preconditionFailure = null;
29+
private $currentTestRun = 0;
30+
private static $HOOK_EXECUTION_INIT = "\n/******** Beginning execution of ActionsInDifferentModulesSuite suite %s block ********/\n";
31+
private static $HOOK_EXECUTION_END = "\n/******** Execution of ActionsInDifferentModulesSuite suite %s block complete ********/\n";
32+
/** @var MagentoWebDriver */
33+
private $webDriver;
34+
/** @var ModuleContainer */
35+
private $moduleContainer;
36+
37+
public function _before(\Codeception\Event\TestEvent $e)
38+
{
39+
$this->webDriver = $this->getModule('\Magento\FunctionalTestingFramework\Module\MagentoWebDriver');
40+
$this->moduleContainer = $this->webDriver->getModuleContainer();
41+
// increment test count per execution
42+
$this->currentTestRun++;
43+
$this->executePreConditions();
44+
45+
if ($this->preconditionFailure != null) {
46+
//if our preconditions fail, we need to mark all the tests as incomplete.
47+
$e->getTest()->getMetadata()->setIncomplete("SUITE PRECONDITION FAILED:" . PHP_EOL . $this->preconditionFailure);
48+
}
49+
}
50+
51+
private function executePreConditions()
52+
{
53+
if ($this->currentTestRun == 1) {
54+
print sprintf(self::$HOOK_EXECUTION_INIT, "before");
55+
56+
try {
57+
if ($this->webDriver->webDriver != null) {
58+
$this->webDriver->_restart();
59+
} else {
60+
$this->webDriver->_initializeSession();
61+
}
62+
$cli = $this->getModuleForAction("magentoCLISecret")->magentoCLISecret($this->getModuleForAction("getSecret")->getSecret("magento/some/secret"), 60); // stepKey: cli
63+
print($cli); // stepKey: cli
64+
$create1Fields['someKey'] = "dataHere";
65+
PersistedObjectHandler::getInstance()->createEntity(
66+
"create1",
67+
"suite",
68+
"SecretData",
69+
[],
70+
$create1Fields
71+
);
72+
PersistedObjectHandler::getInstance()->createEntity(
73+
"create2",
74+
"suite",
75+
"SecretData",
76+
[]
77+
);
78+
PersistedObjectHandler::getInstance()->createEntity(
79+
"create3",
80+
"suite",
81+
"SecretData",
82+
["create1", "create2"]
83+
);
84+
$this->getModuleForAction("fillSecretField")->fillSecretField("#fill", $this->getModuleForAction("getSecret")->getSecret("magento/some/secret") . "+" . $this->getModuleForAction("getSecret")->getSecret("magento/some/secret")); // stepKey: fillBefore
85+
$this->getModuleForAction("click")->click(PersistedObjectHandler::getInstance()->retrieveEntityField('create2', 'key2', 'suite')); // stepKey: click
86+
print("Entering Action Group [return1] ActionGroupReturningValueActionGroup");
87+
$grabProducts1Return1 = $this->getModuleForAction("grabMultiple")->grabMultiple("selector"); // stepKey: grabProducts1Return1
88+
$this->getModuleForAction("assertCount")->assertCount(1, $grabProducts1Return1); // stepKey: assertCountReturn1
89+
$return1 = $this->getModuleForAction("return")->return($grabProducts1Return1); // stepKey: returnProducts1Return1
90+
print("Exiting Action Group [return1] ActionGroupReturningValueActionGroup");
91+
} catch (\Exception $exception) {
92+
$this->preconditionFailure = $exception->getMessage();
93+
}
94+
95+
// reset configuration and close session
96+
$this->webDriver->_resetConfig();
97+
$this->webDriver->webDriver->close();
98+
$this->webDriver->webDriver = null;
99+
100+
print sprintf(self::$HOOK_EXECUTION_END, "before");
101+
}
102+
}
103+
104+
public function _after(\Codeception\Event\TestEvent $e)
105+
{
106+
$this->executePostConditions($e);
107+
}
108+
109+
private function executePostConditions(\Codeception\Event\TestEvent $e)
110+
{
111+
if ($this->currentTestRun == $this->testCount) {
112+
print sprintf(self::$HOOK_EXECUTION_INIT, "after");
113+
114+
try {
115+
// Find out if Test in Suite failed, will cause potential failures in suite after
116+
$cest = $e->getTest();
117+
118+
//Access private TestResultObject to find stack and if there are any errors (as opposed to failures)
119+
$testResultObject = call_user_func(\Closure::bind(
120+
function () use ($cest) {
121+
return $cest->getTestResultObject();
122+
},
123+
$cest
124+
));
125+
$errors = $testResultObject->errors();
126+
127+
if (!empty($errors)) {
128+
foreach ($errors as $error) {
129+
if ($error->failedTest()->getTestMethod() == $cest->getName()) {
130+
// Do not attempt to run _after if failure was in the _after block
131+
// Try to run _after but catch exceptions to prevent them from overwriting original failure.
132+
print("LAST TEST IN SUITE FAILED, TEST AFTER MAY NOT BE SUCCESSFUL\n");
133+
}
134+
}
135+
}
136+
if ($this->webDriver->webDriver != null) {
137+
$this->webDriver->_restart();
138+
} else {
139+
$this->webDriver->_initializeSession();
140+
}
141+
print("Entering Action Group [return2] ExtendedActionGroupReturningValueActionGroup");
142+
$grabProducts1Return2 = $this->getModuleForAction("grabMultiple")->grabMultiple("selector"); // stepKey: grabProducts1Return2
143+
$this->getModuleForAction("assertCount")->assertCount(1, $grabProducts1Return2); // stepKey: assertCountReturn2
144+
$return2 = $this->getModuleForAction("return")->return($grabProducts1Return2); // stepKey: returnProducts1Return2
145+
$grabProducts2Return2 = $this->getModuleForAction("grabMultiple")->grabMultiple("otherSelector"); // stepKey: grabProducts2Return2
146+
$this->getModuleForAction("assertCount")->assertCount(2, $grabProducts2Return2); // stepKey: assertSecondCountReturn2
147+
$return2 = $this->getModuleForAction("return")->return($grabProducts2Return2); // stepKey: returnProducts2Return2
148+
print("Exiting Action Group [return2] ExtendedActionGroupReturningValueActionGroup");
149+
PersistedObjectHandler::getInstance()->deleteEntity(
150+
"create1",
151+
"suite"
152+
);
153+
PersistedObjectHandler::getInstance()->deleteEntity(
154+
"create2",
155+
"suite"
156+
);
157+
PersistedObjectHandler::getInstance()->deleteEntity(
158+
"create3",
159+
"suite"
160+
);
161+
$this->getModuleForAction("deleteEntityByUrl")->deleteEntityByUrl("deleteThis"); // stepKey: deleteThis
162+
$this->getModuleForAction("fillSecretField")->fillSecretField("#fill", $this->getModuleForAction("getSecret")->getSecret("magento/some/secret")); // stepKey: fillAfter
163+
$cli2 = $this->getModuleForAction("magentoCLISecret")->magentoCLISecret($this->getModuleForAction("getSecret")->getSecret("magento/some/secret") . "-some/data-" . $this->getModuleForAction("getSecret")->getSecret("magento/some/secret"), 60); // stepKey: cli2
164+
print($cli2); // stepKey: cli2
165+
} catch (\Exception $exception) {
166+
print $exception->getMessage();
167+
}
168+
169+
PersistedObjectHandler::getInstance()->clearSuiteObjects();
170+
171+
$this->closeSession($this->webDriver);
172+
173+
print sprintf(self::$HOOK_EXECUTION_END, "after");
174+
}
175+
}
176+
177+
/**
178+
* Close session method closes current session.
179+
* If config 'close_all_sessions' is set to 'true' all sessions will be closed.
180+
*
181+
* return void
182+
*/
183+
private function closeSession(): void
184+
{
185+
$webDriverConfig = $this->webDriver->_getConfig();
186+
$this->webDriver->_closeSession();
187+
if (isset($webDriverConfig['close_all_sessions']) && $webDriverConfig['close_all_sessions'] === "true") {
188+
$wdHost = sprintf(
189+
'%s://%s:%s%s',
190+
$webDriverConfig['protocol'],
191+
$webDriverConfig['host'],
192+
$webDriverConfig['port'],
193+
$webDriverConfig['path']
194+
);
195+
$availableSessions = RemoteWebDriver::getAllSessions($wdHost);
196+
foreach ($availableSessions as $session) {
197+
$remoteWebDriver = RemoteWebDriver::createBySessionID($session['id'], $wdHost);
198+
$remoteWebDriver->quit();
199+
}
200+
}
201+
}
202+
203+
/**
204+
* Return the module for an action.
205+
*
206+
* @param string $action
207+
* @return Module
208+
* @throws \Exception
209+
*/
210+
private function getModuleForAction($action)
211+
{
212+
$module = $this->moduleContainer->moduleForAction($action);
213+
if ($module === null) {
214+
throw new TestFrameworkException('Invalid action "' . $action . '"' . PHP_EOL);
215+
}
216+
return $module;
217+
}
218+
}

dev/tests/verification/Resources/functionalSuiteHooks.txt

Lines changed: 46 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,11 @@ use Facebook\WebDriver\Remote\RemoteWebDriver;
66
use Magento\FunctionalTestingFramework\DataGenerator\Handlers\PersistedObjectHandler;
77
use Magento\FunctionalTestingFramework\DataGenerator\Handlers\CredentialStore;
88
use Magento\FunctionalTestingFramework\Module\MagentoWebDriver;
9+
use Magento\FunctionalTestingFramework\Module\MagentoAssert;
10+
use Magento\FunctionalTestingFramework\Module\MagentoActionProxies;
11+
use Magento\FunctionalTestingFramework\Exceptions\TestFrameworkException;
12+
use Codeception\Lib\ModuleContainer;
13+
use Codeception\Module;
914

1015
/**
1116
* Group class is Codeception Extension which is allowed to handle to all internal events.
@@ -24,9 +29,15 @@ class functionalSuiteHooks extends \Codeception\GroupObject
2429
private $currentTestRun = 0;
2530
private static $HOOK_EXECUTION_INIT = "\n/******** Beginning execution of functionalSuiteHooks suite %s block ********/\n";
2631
private static $HOOK_EXECUTION_END = "\n/******** Execution of functionalSuiteHooks suite %s block complete ********/\n";
32+
/** @var MagentoWebDriver */
33+
private $webDriver;
34+
/** @var ModuleContainer */
35+
private $moduleContainer;
2736

2837
public function _before(\Codeception\Event\TestEvent $e)
2938
{
39+
$this->webDriver = $this->getModule('\Magento\FunctionalTestingFramework\Module\MagentoWebDriver');
40+
$this->moduleContainer = $this->webDriver->getModuleContainer();
3041
// increment test count per execution
3142
$this->currentTestRun++;
3243
$this->executePreConditions();
@@ -37,22 +48,18 @@ class functionalSuiteHooks extends \Codeception\GroupObject
3748
}
3849
}
3950

40-
4151
private function executePreConditions()
4252
{
4353
if ($this->currentTestRun == 1) {
4454
print sprintf(self::$HOOK_EXECUTION_INIT, "before");
4555

46-
/** @var MagentoWebDriver $webDriver */
47-
$webDriver = $this->getModule('\Magento\FunctionalTestingFramework\Module\MagentoWebDriver');
48-
4956
try {
50-
if ($webDriver->webDriver != null) {
51-
$webDriver->_restart();
57+
if ($this->webDriver->webDriver != null) {
58+
$this->webDriver->_restart();
5259
} else {
53-
$webDriver->_initializeSession();
60+
$this->webDriver->_initializeSession();
5461
}
55-
$webDriver->amOnPage("some.url"); // stepKey: before
62+
$this->getModuleForAction("amOnPage")->amOnPage("some.url"); // stepKey: before
5663
$createOneFields['someKey'] = "dataHere";
5764
PersistedObjectHandler::getInstance()->createEntity(
5865
"createOne",
@@ -79,18 +86,18 @@ class functionalSuiteHooks extends \Codeception\GroupObject
7986
"createEntityFour",
8087
["createEntityTwo", "createEntityThree"]
8188
);
82-
$webDriver->click(PersistedObjectHandler::getInstance()->retrieveEntityField('createTwo', 'data', 'suite')); // stepKey: clickWithData
89+
$this->getModuleForAction("click")->click(PersistedObjectHandler::getInstance()->retrieveEntityField('createTwo', 'data', 'suite')); // stepKey: clickWithData
8390
print("Entering Action Group [AC] actionGroupWithTwoArguments");
84-
$webDriver->see("John", msq("uniqueData") . "John"); // stepKey: seeFirstNameAC
91+
$this->getModuleForAction("see")->see("John", msq("uniqueData") . "John"); // stepKey: seeFirstNameAC
8592
print("Exiting Action Group [AC] actionGroupWithTwoArguments");
8693
} catch (\Exception $exception) {
8794
$this->preconditionFailure = $exception->getMessage();
8895
}
8996

9097
// reset configuration and close session
91-
$webDriver->_resetConfig();
92-
$webDriver->webDriver->close();
93-
$webDriver->webDriver = null;
98+
$this->webDriver->_resetConfig();
99+
$this->webDriver->webDriver->close();
100+
$this->webDriver->webDriver = null;
94101

95102
print sprintf(self::$HOOK_EXECUTION_END, "before");
96103
}
@@ -101,15 +108,11 @@ class functionalSuiteHooks extends \Codeception\GroupObject
101108
$this->executePostConditions($e);
102109
}
103110

104-
105111
private function executePostConditions(\Codeception\Event\TestEvent $e)
106112
{
107113
if ($this->currentTestRun == $this->testCount) {
108114
print sprintf(self::$HOOK_EXECUTION_INIT, "after");
109115

110-
/** @var MagentoWebDriver $webDriver */
111-
$webDriver = $this->getModule('\Magento\FunctionalTestingFramework\Module\MagentoWebDriver');
112-
113116
try {
114117
// Find out if Test in Suite failed, will cause potential failures in suite after
115118
$cest = $e->getTest();
@@ -132,23 +135,23 @@ class functionalSuiteHooks extends \Codeception\GroupObject
132135
}
133136
}
134137
}
135-
if ($webDriver->webDriver != null) {
136-
$webDriver->_restart();
138+
if ($this->webDriver->webDriver != null) {
139+
$this->webDriver->_restart();
137140
} else {
138-
$webDriver->_initializeSession();
141+
$this->webDriver->_initializeSession();
139142
}
140-
$webDriver->amOnPage("some.url"); // stepKey: after
141-
$webDriver->deleteEntityByUrl("deleteThis"); // stepKey: delete
143+
$this->getModuleForAction("amOnPage")->amOnPage("some.url"); // stepKey: after
144+
$this->getModuleForAction("deleteEntityByUrl")->deleteEntityByUrl("deleteThis"); // stepKey: delete
142145
print("Entering Action Group [AC] actionGroupWithTwoArguments");
143-
$webDriver->see("John", msq("uniqueData") . "John"); // stepKey: seeFirstNameAC
146+
$this->getModuleForAction("see")->see("John", msq("uniqueData") . "John"); // stepKey: seeFirstNameAC
144147
print("Exiting Action Group [AC] actionGroupWithTwoArguments");
145148
} catch (\Exception $exception) {
146149
print $exception->getMessage();
147150
}
148151

149152
PersistedObjectHandler::getInstance()->clearSuiteObjects();
150153

151-
$this->closeSession($webDriver);
154+
$this->closeSession($this->webDriver);
152155

153156
print sprintf(self::$HOOK_EXECUTION_END, "after");
154157
}
@@ -158,13 +161,12 @@ class functionalSuiteHooks extends \Codeception\GroupObject
158161
* Close session method closes current session.
159162
* If config 'close_all_sessions' is set to 'true' all sessions will be closed.
160163
*
161-
* @param MagentoWebDriver $webDriver
162164
* return void
163165
*/
164-
private function closeSession(MagentoWebDriver $webDriver): void
166+
private function closeSession(): void
165167
{
166-
$webDriverConfig = $webDriver->_getConfig();
167-
$webDriver->_closeSession();
168+
$webDriverConfig = $this->webDriver->_getConfig();
169+
$this->webDriver->_closeSession();
168170
if (isset($webDriverConfig['close_all_sessions']) && $webDriverConfig['close_all_sessions'] === "true") {
169171
$wdHost = sprintf(
170172
'%s://%s:%s%s',
@@ -180,4 +182,20 @@ class functionalSuiteHooks extends \Codeception\GroupObject
180182
}
181183
}
182184
}
185+
186+
/**
187+
* Return the module for an action.
188+
*
189+
* @param string $action
190+
* @return Module
191+
* @throws \Exception
192+
*/
193+
private function getModuleForAction($action)
194+
{
195+
$module = $this->moduleContainer->moduleForAction($action);
196+
if ($module === null) {
197+
throw new TestFrameworkException('Invalid action "' . $action . '"' . PHP_EOL);
198+
}
199+
return $module;
200+
}
183201
}

0 commit comments

Comments
 (0)