Skip to content

Commit c41a910

Browse files
authored
Merge pull request #538 from lbajsarowicz/feature/cron-command
FEATURE: <magentoCron> command to execute Cron Jobs
2 parents 231e58d + f3bb5c2 commit c41a910

File tree

7 files changed

+203
-3
lines changed

7 files changed

+203
-3
lines changed

dev/tests/verification/Resources/BasicFunctionalTest.txt

+6
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,12 @@ class BasicFunctionalTestCest
126126
$I->comment($magentoCli3); // stepKey: magentoCli3
127127
$magentoCli4 = $I->magentoCLISecret("config:set somePath " . $I->getSecret("someKey"), 120); // stepKey: magentoCli4
128128
$I->comment($magentoCli4); // stepKey: magentoCli4
129+
$cronAllGroups = $I->magentoCron("", 70); // stepKey: cronAllGroups
130+
$I->comment($cronAllGroups);
131+
$cronSingleGroup = $I->magentoCron("index", 70); // stepKey: cronSingleGroup
132+
$I->comment($cronSingleGroup);
133+
$cronMultipleGroups = $I->magentoCron("a b c", 70); // stepKey: cronMultipleGroups
134+
$I->comment($cronMultipleGroups);
129135
$I->makeScreenshot("screenShotInput"); // stepKey: makeScreenshotKey1
130136
$I->maximizeWindow(); // stepKey: maximizeWindowKey1
131137
$I->moveBack(); // stepKey: moveBackKey1

dev/tests/verification/TestModule/Test/BasicFunctionalTest.xml

+4-1
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,9 @@
7979
<magentoCLI command="maintenance:enable" arguments="&quot;stuffHere&quot;" timeout="120" stepKey="magentoCli2"/>
8080
<magentoCLI command="config:set somePath {{_CREDS.someKey}}" stepKey="magentoCli3"/>
8181
<magentoCLI command="config:set somePath {{_CREDS.someKey}}" timeout="120" stepKey="magentoCli4"/>
82+
<magentoCron stepKey="cronAllGroups"/>
83+
<magentoCron groups="index" stepKey="cronSingleGroup"/>
84+
<magentoCron groups="a b c" stepKey="cronMultipleGroups"/>
8285
<makeScreenshot userInput="screenShotInput" stepKey="makeScreenshotKey1"/>
8386
<maximizeWindow stepKey="maximizeWindowKey1"/>
8487
<moveBack stepKey="moveBackKey1"/>
@@ -143,4 +146,4 @@
143146
<fillField selector="#bar" userInput="bar" stepKey="fillField2"/>
144147
<fillField selector="#baz" userInput="baz" stepKey="fillField3"/>
145148
</test>
146-
</tests>
149+
</tests>

docs/test/actions.md

+23
Original file line numberDiff line numberDiff line change
@@ -1274,6 +1274,29 @@ Attribute|Type|Use|Description
12741274
<magentoCLI command="indexer:reindex" stepKey="reindex"/>
12751275
```
12761276

1277+
### magentoCron
1278+
1279+
Used to execute Magento Cron jobs. Groups may be provided optionally. Internal mechanism of `<magentoCron>` ensures that Cron Job of single group is ran with 60 seconds interval.
1280+
1281+
Attribute|Type|Use|Description
1282+
---|---|---|---
1283+
`groups`|string |optional| Run only specified groups of Cron Jobs
1284+
`arguments`|string |optional| Unescaped arguments to be passed in with the CLI command.
1285+
`timeout`|string|optional| Number of seconds CLI command can run without outputting anything.
1286+
`stepKey`|string|required| A unique identifier of the action.
1287+
`before`|string|optional| `stepKey` of action that must be executed next.
1288+
`after`|string|optional| `stepKey` of preceding action.
1289+
1290+
1291+
#### Example
1292+
```xml
1293+
<magentoCron stepKey="runStagingCronJobs" groups="staging"/>
1294+
<!-- No interval here -->
1295+
<magentoCron stepKey="runIndexCronJobs" groups="index"/>
1296+
<!-- 60 seconds interval takes place here -->
1297+
<magentoCron stepKey="runAllCronJobs"/>
1298+
```
1299+
12771300
### makeScreenshot
12781301

12791302
See [makeScreenshot docs on codeception.com](http://codeception.com/docs/modules/WebDriver#makeScreenshot).

etc/di.xml

+1-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88

99
<!-- Entity value gets replaced in Dom.php before reading $xml -->
1010
<!DOCTYPE config [
11-
<!ENTITY commonTestActions "acceptPopup|actionGroup|amOnPage|amOnUrl|amOnSubdomain|appendField|assertArrayIsSorted|assertArraySubset|assertElementContainsAttribute|attachFile|cancelPopup|checkOption|clearField|click|clickWithLeftButton|clickWithRightButton|closeAdminNotification|closeTab|comment|conditionalClick|createData|deleteData|updateData|getData|dontSee|dontSeeJsError|dontSeeCheckboxIsChecked|dontSeeCookie|dontSeeCurrentUrlEquals|dontSeeCurrentUrlMatches|dontSeeElement|dontSeeElementInDOM|dontSeeInCurrentUrl|dontSeeInField|dontSeeInFormFields|dontSeeInPageSource|dontSeeInSource|dontSeeInTitle|dontSeeLink|dontSeeOptionIsSelected|doubleClick|dragAndDrop|entity|executeJS|executeInSelenium|fillField|formatMoney|generateDate|grabAttributeFrom|grabCookie|grabFromCurrentUrl|grabMultiple|grabPageSource|grabTextFrom|grabValueFrom|loadSessionSnapshot|loginAsAdmin|magentoCLI|makeScreenshot|maximizeWindow|moveBack|moveForward|moveMouseOver|mSetLocale|mResetLocale|openNewTab|pauseExecution|parseFloat|performOn|pressKey|reloadPage|resetCookie|submitForm|resizeWindow|saveSessionSnapshot|scrollTo|scrollToTopOfPage|searchAndMultiSelectOption|see|seeCheckboxIsChecked|seeCookie|seeCurrentUrlEquals|seeCurrentUrlMatches|seeElement|seeElementInDOM|seeInCurrentUrl|seeInField|seeInFormFields|seeInPageSource|seeInPopup|seeInSource|seeInTitle|seeLink|seeNumberOfElements|seeOptionIsSelected|selectOption|setCookie|submitForm|switchToIFrame|switchToNextTab|switchToPreviousTab|switchToWindow|typeInPopup|uncheckOption|unselectOption|wait|waitForAjaxLoad|waitForElement|waitForElementChange|waitForElementNotVisible|waitForElementVisible|waitForPwaElementNotVisible|waitForPwaElementVisible|waitForJS|waitForLoadingMaskToDisappear|waitForPageLoad|waitForText|assertArrayHasKey|assertArrayNotHasKey|assertArraySubset|assertContains|assertCount|assertEmpty|assertEquals|assertFalse|assertFileExists|assertFileNotExists|assertGreaterOrEquals|assertGreaterThan|assertGreaterThanOrEqual|assertInstanceOf|assertInternalType|assertIsEmpty|assertLessOrEquals|assertLessThan|assertLessThanOrEqual|assertNotContains|assertNotEmpty|assertNotEquals|assertNotInstanceOf|assertNotNull|assertNotRegExp|assertNotSame|assertNull|assertRegExp|assertSame|assertStringStartsNotWith|assertStringStartsWith|assertTrue|expectException|fail|dontSeeFullUrlEquals|dontSee|dontSeeFullUrlMatches|dontSeeInFullUrl|seeFullUrlEquals|seeFullUrlMatches|seeInFullUrl|grabFromFullUrl">
11+
<!ENTITY commonTestActions "acceptPopup|actionGroup|amOnPage|amOnUrl|amOnSubdomain|appendField|assertArrayIsSorted|assertArraySubset|assertElementContainsAttribute|attachFile|cancelPopup|checkOption|clearField|click|clickWithLeftButton|clickWithRightButton|closeAdminNotification|closeTab|comment|conditionalClick|createData|deleteData|updateData|getData|dontSee|dontSeeJsError|dontSeeCheckboxIsChecked|dontSeeCookie|dontSeeCurrentUrlEquals|dontSeeCurrentUrlMatches|dontSeeElement|dontSeeElementInDOM|dontSeeInCurrentUrl|dontSeeInField|dontSeeInFormFields|dontSeeInPageSource|dontSeeInSource|dontSeeInTitle|dontSeeLink|dontSeeOptionIsSelected|doubleClick|dragAndDrop|entity|executeJS|executeInSelenium|fillField|formatMoney|generateDate|grabAttributeFrom|grabCookie|grabFromCurrentUrl|grabMultiple|grabPageSource|grabTextFrom|grabValueFrom|loadSessionSnapshot|loginAsAdmin|magentoCLI|magentoCron|makeScreenshot|maximizeWindow|moveBack|moveForward|moveMouseOver|mSetLocale|mResetLocale|openNewTab|pauseExecution|parseFloat|performOn|pressKey|reloadPage|resetCookie|submitForm|resizeWindow|saveSessionSnapshot|scrollTo|scrollToTopOfPage|searchAndMultiSelectOption|see|seeCheckboxIsChecked|seeCookie|seeCurrentUrlEquals|seeCurrentUrlMatches|seeElement|seeElementInDOM|seeInCurrentUrl|seeInField|seeInFormFields|seeInPageSource|seeInPopup|seeInSource|seeInTitle|seeLink|seeNumberOfElements|seeOptionIsSelected|selectOption|setCookie|submitForm|switchToIFrame|switchToNextTab|switchToPreviousTab|switchToWindow|typeInPopup|uncheckOption|unselectOption|wait|waitForAjaxLoad|waitForElement|waitForElementChange|waitForElementNotVisible|waitForElementVisible|waitForPwaElementNotVisible|waitForPwaElementVisible|waitForJS|waitForLoadingMaskToDisappear|waitForPageLoad|waitForText|assertArrayHasKey|assertArrayNotHasKey|assertArraySubset|assertContains|assertCount|assertEmpty|assertEquals|assertFalse|assertFileExists|assertFileNotExists|assertGreaterOrEquals|assertGreaterThan|assertGreaterThanOrEqual|assertInstanceOf|assertInternalType|assertIsEmpty|assertLessOrEquals|assertLessThan|assertLessThanOrEqual|assertNotContains|assertNotEmpty|assertNotEquals|assertNotInstanceOf|assertNotNull|assertNotRegExp|assertNotSame|assertNull|assertRegExp|assertSame|assertStringStartsNotWith|assertStringStartsWith|assertTrue|expectException|fail|dontSeeFullUrlEquals|dontSee|dontSeeFullUrlMatches|dontSeeInFullUrl|seeFullUrlEquals|seeFullUrlMatches|seeInFullUrl|grabFromFullUrl">
1212
]>
1313

1414
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../src/Magento/FunctionalTestingFramework/ObjectManager/etc/config.xsd">

src/Magento/FunctionalTestingFramework/Module/MagentoWebDriver.php

+111
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,9 @@ class MagentoWebDriver extends WebDriver
5353
{
5454
use AttachmentSupport;
5555

56+
const MAGENTO_CRON_INTERVAL = 60;
57+
const MAGENTO_CRON_COMMAND = 'cron:run';
58+
5659
/**
5760
* List of known magento loading masks by selector
5861
*
@@ -121,6 +124,13 @@ class MagentoWebDriver extends WebDriver
121124
*/
122125
private $jsErrors = [];
123126

127+
/**
128+
* Contains last execution times for Cron
129+
*
130+
* @var int[]
131+
*/
132+
private $cronExecution = [];
133+
124134
/**
125135
* Sanitizes config, then initializes using parent.
126136
*
@@ -552,6 +562,75 @@ public function magentoCLI($command, $timeout = null, $arguments = null)
552562
return $response;
553563
}
554564

565+
/**
566+
* Executes Magento Cron keeping the interval (> 60 seconds between each run)
567+
*
568+
* @param string|null $cronGroups
569+
* @param integer|null $timeout
570+
* @param string|null $arguments
571+
* @return string
572+
*/
573+
public function magentoCron($cronGroups = null, $timeout = null, $arguments = null)
574+
{
575+
$cronGroups = explode(' ', $cronGroups);
576+
return $this->executeCronjobs($cronGroups, $timeout, $arguments);
577+
}
578+
579+
/**
580+
* Updates last execution time for Cron
581+
*
582+
* @param array $cronGroups
583+
* @return void
584+
*/
585+
private function notifyCronFinished(array $cronGroups = [])
586+
{
587+
if (empty($cronGroups)) {
588+
$this->cronExecution['*'] = time();
589+
}
590+
591+
foreach ($cronGroups as $group) {
592+
$this->cronExecution[$group] = time();
593+
}
594+
}
595+
596+
/**
597+
* Returns last Cron execution time for specific cron or all crons
598+
*
599+
* @param array $cronGroups
600+
* @return integer
601+
*/
602+
private function getLastCronExecution(array $cronGroups = [])
603+
{
604+
if (empty($cronGroups)) {
605+
return (int)max($this->cronExecution);
606+
}
607+
608+
$cronGroups = array_merge($cronGroups, ['*']);
609+
610+
return array_reduce($cronGroups, function ($lastExecution, $group) {
611+
if (isset($this->cronExecution[$group]) && $this->cronExecution[$group] > $lastExecution) {
612+
$lastExecution = $this->cronExecution[$group];
613+
}
614+
615+
return (int)$lastExecution;
616+
}, 0);
617+
}
618+
619+
/**
620+
* Returns time to wait for next run
621+
*
622+
* @param array $cronGroups
623+
* @param integer $cronInterval
624+
* @return integer
625+
*/
626+
private function getCronWait(array $cronGroups = [], int $cronInterval = self::MAGENTO_CRON_INTERVAL)
627+
{
628+
$nextRun = $this->getLastCronExecution($cronGroups) + $cronInterval;
629+
$toNextRun = $nextRun - time();
630+
631+
return max(0, $toNextRun);
632+
}
633+
555634
/**
556635
* Runs DELETE request to delete a Magento entity against the url given.
557636
*
@@ -971,4 +1050,36 @@ public function getSecret($key)
9711050
{
9721051
return CredentialStore::getInstance()->getSecret($key);
9731052
}
1053+
1054+
/**
1055+
* Waits proper amount of time to perform Cron execution
1056+
*
1057+
* @param string $cronGroups
1058+
* @param integer $timeout
1059+
* @param string $arguments
1060+
* @return string
1061+
* @throws TestFrameworkException
1062+
*/
1063+
private function executeCronjobs($cronGroups, $timeout, $arguments): string
1064+
{
1065+
$cronGroups = array_filter($cronGroups);
1066+
1067+
$waitFor = $this->getCronWait($cronGroups);
1068+
1069+
if ($waitFor) {
1070+
$this->wait($waitFor);
1071+
}
1072+
1073+
$command = array_reduce($cronGroups, function ($command, $cronGroup) {
1074+
$command .= ' --group=' . $cronGroup;
1075+
return $command;
1076+
}, self::MAGENTO_CRON_COMMAND);
1077+
$timeStart = microtime(true);
1078+
$cronResult = $this->magentoCLI($command, $timeout, $arguments);
1079+
$timeEnd = microtime(true);
1080+
1081+
$this->notifyCronFinished($cronGroups);
1082+
1083+
return sprintf('%s (wait: %ss, execution: %ss)', $cronResult, $waitFor, round($timeEnd - $timeStart, 2));
1084+
}
9741085
}

src/Magento/FunctionalTestingFramework/Test/etc/Actions/customActions.xsd

+36-1
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
<xs:group name="customTags">
1313
<xs:choice>
1414
<xs:element type="magentoCLIType" name="magentoCLI" minOccurs="0" maxOccurs="unbounded"/>
15+
<xs:element type="magentoCronType" name="magentoCron" minOccurs="0" maxOccurs="unbounded"/>
1516
<xs:element type="closeAdminNotificationType" name="closeAdminNotification" minOccurs="0" maxOccurs="unbounded"/>
1617
<xs:element type="searchAndMultiSelectOptionType" name="searchAndMultiSelectOption" minOccurs="0" maxOccurs="unbounded"/>
1718
<xs:element type="selectMultipleOptionsType" name="selectMultipleOptions" minOccurs="0" maxOccurs="unbounded"/>
@@ -62,6 +63,40 @@
6263
</xs:simpleContent>
6364
</xs:complexType>
6465

66+
<xs:complexType name="magentoCronType">
67+
<xs:annotation>
68+
<xs:documentation>
69+
Executes Magento Cron Jobs (selected groups)
70+
</xs:documentation>
71+
</xs:annotation>
72+
<xs:simpleContent>
73+
<xs:extension base="xs:string">
74+
<xs:attribute type="xs:string" name="groups">
75+
<xs:annotation>
76+
<xs:documentation>
77+
Cron groups to be executed (separated by space)
78+
</xs:documentation>
79+
</xs:annotation>
80+
</xs:attribute>
81+
<xs:attribute type="xs:string" name="arguments">
82+
<xs:annotation>
83+
<xs:documentation>
84+
Arguments for Magento CLI command, will not be escaped.
85+
</xs:documentation>
86+
</xs:annotation>
87+
</xs:attribute>
88+
<xs:attribute type="xs:string" name="timeout">
89+
<xs:annotation>
90+
<xs:documentation>
91+
Idle timeout in seconds, defaulted to 60s when not specified.
92+
</xs:documentation>
93+
</xs:annotation>
94+
</xs:attribute>
95+
<xs:attributeGroup ref="commonActionAttributes"/>
96+
</xs:extension>
97+
</xs:simpleContent>
98+
</xs:complexType>
99+
65100
<xs:complexType name="closeAdminNotificationType">
66101
<xs:annotation>
67102
<xs:documentation>
@@ -285,4 +320,4 @@
285320
<xs:enumeration value="desc"/>
286321
</xs:restriction>
287322
</xs:simpleType>
288-
</xs:schema>
323+
</xs:schema>

src/Magento/FunctionalTestingFramework/Util/TestGenerator.php

+22
Original file line numberDiff line numberDiff line change
@@ -52,12 +52,14 @@ class TestGenerator
5252
'retrieveEntityField',
5353
'getSecret',
5454
'magentoCLI',
55+
'magentoCron',
5556
'generateDate',
5657
'field'
5758
];
5859
const RULE_ERROR = 'On step with stepKey "%s", only one of the attributes: "%s" can be use for action "%s"';
5960

6061
const STEP_KEY_ANNOTATION = " // stepKey: %s";
62+
const CRON_INTERVAL = 60;
6163
const ARRAY_WRAP_OPEN = '[';
6264
const ARRAY_WRAP_CLOSE = ']';
6365

@@ -538,6 +540,7 @@ public function generateStepsPhp($actionObjects, $generationScope = TestGenerato
538540
$dependentSelector = null;
539541
$visible = null;
540542
$command = null;
543+
$cronGroups = '';
541544
$arguments = null;
542545
$sortOrder = null;
543546
$storeCode = null;
@@ -555,6 +558,9 @@ public function generateStepsPhp($actionObjects, $generationScope = TestGenerato
555558
if (isset($customActionAttributes['command'])) {
556559
$command = $this->addUniquenessFunctionCall($customActionAttributes['command']);
557560
}
561+
if (isset($customActionAttributes['groups'])) {
562+
$cronGroups = $this->addUniquenessFunctionCall($customActionAttributes['groups']);
563+
}
558564
if (isset($customActionAttributes['arguments'])) {
559565
$arguments = $this->addUniquenessFunctionCall($customActionAttributes['arguments']);
560566
}
@@ -1252,6 +1258,22 @@ public function generateStepsPhp($actionObjects, $generationScope = TestGenerato
12521258
$stepKey
12531259
);
12541260
break;
1261+
case 'magentoCron':
1262+
$testSteps .= $this->wrapFunctionCallWithReturnValue(
1263+
$stepKey,
1264+
$actor,
1265+
$actionObject,
1266+
$cronGroups,
1267+
self::CRON_INTERVAL + $time,
1268+
$arguments
1269+
);
1270+
$testSteps .= sprintf(self::STEP_KEY_ANNOTATION, $stepKey) . PHP_EOL;
1271+
$testSteps .= sprintf(
1272+
"\t\t$%s->comment(\$%s);",
1273+
$actor,
1274+
$stepKey
1275+
);
1276+
break;
12551277
case "field":
12561278
$fieldKey = $actionObject->getCustomActionAttributes()['key'];
12571279
$input = $this->resolveStepKeyReferences($input, $actionObject->getActionOrigin());

0 commit comments

Comments
 (0)