diff --git a/dev/tests/verification/Resources/BasicFunctionalTest.txt b/dev/tests/verification/Resources/BasicFunctionalTest.txt
index 5bcfd55dd..e78f1b49c 100644
--- a/dev/tests/verification/Resources/BasicFunctionalTest.txt
+++ b/dev/tests/verification/Resources/BasicFunctionalTest.txt
@@ -126,6 +126,12 @@ class BasicFunctionalTestCest
$I->comment($magentoCli3); // stepKey: magentoCli3
$magentoCli4 = $I->magentoCLISecret("config:set somePath " . $I->getSecret("someKey"), 120); // stepKey: magentoCli4
$I->comment($magentoCli4); // stepKey: magentoCli4
+ $cronAllGroups = $I->magentoCron("", 70); // stepKey: cronAllGroups
+ $I->comment($cronAllGroups);
+ $cronSingleGroup = $I->magentoCron("index", 70); // stepKey: cronSingleGroup
+ $I->comment($cronSingleGroup);
+ $cronMultipleGroups = $I->magentoCron("a b c", 70); // stepKey: cronMultipleGroups
+ $I->comment($cronMultipleGroups);
$I->makeScreenshot("screenShotInput"); // stepKey: makeScreenshotKey1
$I->maximizeWindow(); // stepKey: maximizeWindowKey1
$I->moveBack(); // stepKey: moveBackKey1
diff --git a/dev/tests/verification/TestModule/Test/BasicFunctionalTest.xml b/dev/tests/verification/TestModule/Test/BasicFunctionalTest.xml
index c23a3ce60..1ef25b43d 100644
--- a/dev/tests/verification/TestModule/Test/BasicFunctionalTest.xml
+++ b/dev/tests/verification/TestModule/Test/BasicFunctionalTest.xml
@@ -79,6 +79,9 @@
+
+
+
@@ -143,4 +146,4 @@
-
\ No newline at end of file
+
diff --git a/docs/test/actions.md b/docs/test/actions.md
index c5dc83fdb..59fd12aa2 100644
--- a/docs/test/actions.md
+++ b/docs/test/actions.md
@@ -1274,6 +1274,29 @@ Attribute|Type|Use|Description
```
+### magentoCron
+
+Used to execute Magento Cron jobs. Groups may be provided optionally. Internal mechanism of `` ensures that Cron Job of single group is ran with 60 seconds interval.
+
+Attribute|Type|Use|Description
+---|---|---|---
+`groups`|string |optional| Run only specified groups of Cron Jobs
+`arguments`|string |optional| Unescaped arguments to be passed in with the CLI command.
+`timeout`|string|optional| Number of seconds CLI command can run without outputting anything.
+`stepKey`|string|required| A unique identifier of the action.
+`before`|string|optional| `stepKey` of action that must be executed next.
+`after`|string|optional| `stepKey` of preceding action.
+
+
+#### Example
+```xml
+
+
+
+
+
+```
+
### makeScreenshot
See [makeScreenshot docs on codeception.com](http://codeception.com/docs/modules/WebDriver#makeScreenshot).
diff --git a/etc/di.xml b/etc/di.xml
index 3e6313bf3..e5e31cf87 100644
--- a/etc/di.xml
+++ b/etc/di.xml
@@ -8,7 +8,7 @@
+
]>
diff --git a/src/Magento/FunctionalTestingFramework/Module/MagentoWebDriver.php b/src/Magento/FunctionalTestingFramework/Module/MagentoWebDriver.php
index a95586b26..f44d7e1a0 100644
--- a/src/Magento/FunctionalTestingFramework/Module/MagentoWebDriver.php
+++ b/src/Magento/FunctionalTestingFramework/Module/MagentoWebDriver.php
@@ -53,6 +53,9 @@ class MagentoWebDriver extends WebDriver
{
use AttachmentSupport;
+ const MAGENTO_CRON_INTERVAL = 60;
+ const MAGENTO_CRON_COMMAND = 'cron:run';
+
/**
* List of known magento loading masks by selector
*
@@ -121,6 +124,13 @@ class MagentoWebDriver extends WebDriver
*/
private $jsErrors = [];
+ /**
+ * Contains last execution times for Cron
+ *
+ * @var int[]
+ */
+ private $cronExecution = [];
+
/**
* Sanitizes config, then initializes using parent.
*
@@ -552,6 +562,75 @@ public function magentoCLI($command, $timeout = null, $arguments = null)
return $response;
}
+ /**
+ * Executes Magento Cron keeping the interval (> 60 seconds between each run)
+ *
+ * @param string|null $cronGroups
+ * @param integer|null $timeout
+ * @param string|null $arguments
+ * @return string
+ */
+ public function magentoCron($cronGroups = null, $timeout = null, $arguments = null)
+ {
+ $cronGroups = explode(' ', $cronGroups);
+ return $this->executeCronjobs($cronGroups, $timeout, $arguments);
+ }
+
+ /**
+ * Updates last execution time for Cron
+ *
+ * @param array $cronGroups
+ * @return void
+ */
+ private function notifyCronFinished(array $cronGroups = [])
+ {
+ if (empty($cronGroups)) {
+ $this->cronExecution['*'] = time();
+ }
+
+ foreach ($cronGroups as $group) {
+ $this->cronExecution[$group] = time();
+ }
+ }
+
+ /**
+ * Returns last Cron execution time for specific cron or all crons
+ *
+ * @param array $cronGroups
+ * @return integer
+ */
+ private function getLastCronExecution(array $cronGroups = [])
+ {
+ if (empty($cronGroups)) {
+ return (int)max($this->cronExecution);
+ }
+
+ $cronGroups = array_merge($cronGroups, ['*']);
+
+ return array_reduce($cronGroups, function ($lastExecution, $group) {
+ if (isset($this->cronExecution[$group]) && $this->cronExecution[$group] > $lastExecution) {
+ $lastExecution = $this->cronExecution[$group];
+ }
+
+ return (int)$lastExecution;
+ }, 0);
+ }
+
+ /**
+ * Returns time to wait for next run
+ *
+ * @param array $cronGroups
+ * @param integer $cronInterval
+ * @return integer
+ */
+ private function getCronWait(array $cronGroups = [], int $cronInterval = self::MAGENTO_CRON_INTERVAL)
+ {
+ $nextRun = $this->getLastCronExecution($cronGroups) + $cronInterval;
+ $toNextRun = $nextRun - time();
+
+ return max(0, $toNextRun);
+ }
+
/**
* Runs DELETE request to delete a Magento entity against the url given.
*
@@ -971,4 +1050,36 @@ public function getSecret($key)
{
return CredentialStore::getInstance()->getSecret($key);
}
+
+ /**
+ * Waits proper amount of time to perform Cron execution
+ *
+ * @param string $cronGroups
+ * @param integer $timeout
+ * @param string $arguments
+ * @return string
+ * @throws TestFrameworkException
+ */
+ private function executeCronjobs($cronGroups, $timeout, $arguments): string
+ {
+ $cronGroups = array_filter($cronGroups);
+
+ $waitFor = $this->getCronWait($cronGroups);
+
+ if ($waitFor) {
+ $this->wait($waitFor);
+ }
+
+ $command = array_reduce($cronGroups, function ($command, $cronGroup) {
+ $command .= ' --group=' . $cronGroup;
+ return $command;
+ }, self::MAGENTO_CRON_COMMAND);
+ $timeStart = microtime(true);
+ $cronResult = $this->magentoCLI($command, $timeout, $arguments);
+ $timeEnd = microtime(true);
+
+ $this->notifyCronFinished($cronGroups);
+
+ return sprintf('%s (wait: %ss, execution: %ss)', $cronResult, $waitFor, round($timeEnd - $timeStart, 2));
+ }
}
diff --git a/src/Magento/FunctionalTestingFramework/Test/etc/Actions/customActions.xsd b/src/Magento/FunctionalTestingFramework/Test/etc/Actions/customActions.xsd
index 2424d0d31..8670d9885 100644
--- a/src/Magento/FunctionalTestingFramework/Test/etc/Actions/customActions.xsd
+++ b/src/Magento/FunctionalTestingFramework/Test/etc/Actions/customActions.xsd
@@ -12,6 +12,7 @@
+
@@ -62,6 +63,40 @@
+
+
+
+ Executes Magento Cron Jobs (selected groups)
+
+
+
+
+
+
+
+ Cron groups to be executed (separated by space)
+
+
+
+
+
+
+ Arguments for Magento CLI command, will not be escaped.
+
+
+
+
+
+
+ Idle timeout in seconds, defaulted to 60s when not specified.
+
+
+
+
+
+
+
+
@@ -285,4 +320,4 @@
-
\ No newline at end of file
+
diff --git a/src/Magento/FunctionalTestingFramework/Util/TestGenerator.php b/src/Magento/FunctionalTestingFramework/Util/TestGenerator.php
index 66c11975c..c67d8288f 100644
--- a/src/Magento/FunctionalTestingFramework/Util/TestGenerator.php
+++ b/src/Magento/FunctionalTestingFramework/Util/TestGenerator.php
@@ -52,12 +52,14 @@ class TestGenerator
'retrieveEntityField',
'getSecret',
'magentoCLI',
+ 'magentoCron',
'generateDate',
'field'
];
const RULE_ERROR = 'On step with stepKey "%s", only one of the attributes: "%s" can be use for action "%s"';
const STEP_KEY_ANNOTATION = " // stepKey: %s";
+ const CRON_INTERVAL = 60;
const ARRAY_WRAP_OPEN = '[';
const ARRAY_WRAP_CLOSE = ']';
@@ -538,6 +540,7 @@ public function generateStepsPhp($actionObjects, $generationScope = TestGenerato
$dependentSelector = null;
$visible = null;
$command = null;
+ $cronGroups = '';
$arguments = null;
$sortOrder = null;
$storeCode = null;
@@ -555,6 +558,9 @@ public function generateStepsPhp($actionObjects, $generationScope = TestGenerato
if (isset($customActionAttributes['command'])) {
$command = $this->addUniquenessFunctionCall($customActionAttributes['command']);
}
+ if (isset($customActionAttributes['groups'])) {
+ $cronGroups = $this->addUniquenessFunctionCall($customActionAttributes['groups']);
+ }
if (isset($customActionAttributes['arguments'])) {
$arguments = $this->addUniquenessFunctionCall($customActionAttributes['arguments']);
}
@@ -1252,6 +1258,22 @@ public function generateStepsPhp($actionObjects, $generationScope = TestGenerato
$stepKey
);
break;
+ case 'magentoCron':
+ $testSteps .= $this->wrapFunctionCallWithReturnValue(
+ $stepKey,
+ $actor,
+ $actionObject,
+ $cronGroups,
+ self::CRON_INTERVAL + $time,
+ $arguments
+ );
+ $testSteps .= sprintf(self::STEP_KEY_ANNOTATION, $stepKey) . PHP_EOL;
+ $testSteps .= sprintf(
+ "\t\t$%s->comment(\$%s);",
+ $actor,
+ $stepKey
+ );
+ break;
case "field":
$fieldKey = $actionObject->getCustomActionAttributes()['key'];
$input = $this->resolveStepKeyReferences($input, $actionObject->getActionOrigin());