Skip to content

FEATURE: <magentoCron> command to execute Cron Jobs #538

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 7 commits into from
Jan 9, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions dev/tests/verification/Resources/BasicFunctionalTest.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,9 @@
<magentoCLI command="maintenance:enable" arguments="&quot;stuffHere&quot;" timeout="120" stepKey="magentoCli2"/>
<magentoCLI command="config:set somePath {{_CREDS.someKey}}" stepKey="magentoCli3"/>
<magentoCLI command="config:set somePath {{_CREDS.someKey}}" timeout="120" stepKey="magentoCli4"/>
<magentoCron stepKey="cronAllGroups"/>
<magentoCron groups="index" stepKey="cronSingleGroup"/>
<magentoCron groups="a b c" stepKey="cronMultipleGroups"/>
<makeScreenshot userInput="screenShotInput" stepKey="makeScreenshotKey1"/>
<maximizeWindow stepKey="maximizeWindowKey1"/>
<moveBack stepKey="moveBackKey1"/>
Expand Down Expand Up @@ -143,4 +146,4 @@
<fillField selector="#bar" userInput="bar" stepKey="fillField2"/>
<fillField selector="#baz" userInput="baz" stepKey="fillField3"/>
</test>
</tests>
</tests>
23 changes: 23 additions & 0 deletions docs/test/actions.md
Original file line number Diff line number Diff line change
Expand Up @@ -1274,6 +1274,29 @@ Attribute|Type|Use|Description
<magentoCLI command="indexer:reindex" stepKey="reindex"/>
```

### magentoCron

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.

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
<magentoCron stepKey="runStagingCronJobs" groups="staging"/>
<!-- No interval here -->
<magentoCron stepKey="runIndexCronJobs" groups="index"/>
<!-- 60 seconds interval takes place here -->
<magentoCron stepKey="runAllCronJobs"/>
```

### makeScreenshot

See [makeScreenshot docs on codeception.com](http://codeception.com/docs/modules/WebDriver#makeScreenshot).
Expand Down
2 changes: 1 addition & 1 deletion etc/di.xml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

<!-- Entity value gets replaced in Dom.php before reading $xml -->
<!DOCTYPE config [
<!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">
<!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">
]>

<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../src/Magento/FunctionalTestingFramework/ObjectManager/etc/config.xsd">
Expand Down
111 changes: 111 additions & 0 deletions src/Magento/FunctionalTestingFramework/Module/MagentoWebDriver.php
Original file line number Diff line number Diff line change
Expand Up @@ -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
*
Expand Down Expand Up @@ -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.
*
Expand Down Expand Up @@ -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.
*
Expand Down Expand Up @@ -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));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
<xs:group name="customTags">
<xs:choice>
<xs:element type="magentoCLIType" name="magentoCLI" minOccurs="0" maxOccurs="unbounded"/>
<xs:element type="magentoCronType" name="magentoCron" minOccurs="0" maxOccurs="unbounded"/>
<xs:element type="closeAdminNotificationType" name="closeAdminNotification" minOccurs="0" maxOccurs="unbounded"/>
<xs:element type="searchAndMultiSelectOptionType" name="searchAndMultiSelectOption" minOccurs="0" maxOccurs="unbounded"/>
<xs:element type="selectMultipleOptionsType" name="selectMultipleOptions" minOccurs="0" maxOccurs="unbounded"/>
Expand Down Expand Up @@ -62,6 +63,40 @@
</xs:simpleContent>
</xs:complexType>

<xs:complexType name="magentoCronType">
<xs:annotation>
<xs:documentation>
Executes Magento Cron Jobs (selected groups)
</xs:documentation>
</xs:annotation>
<xs:simpleContent>
<xs:extension base="xs:string">
<xs:attribute type="xs:string" name="groups">
<xs:annotation>
<xs:documentation>
Cron groups to be executed (separated by space)
</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute type="xs:string" name="arguments">
<xs:annotation>
<xs:documentation>
Arguments for Magento CLI command, will not be escaped.
</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute type="xs:string" name="timeout">
<xs:annotation>
<xs:documentation>
Idle timeout in seconds, defaulted to 60s when not specified.
</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attributeGroup ref="commonActionAttributes"/>
</xs:extension>
</xs:simpleContent>
</xs:complexType>

<xs:complexType name="closeAdminNotificationType">
<xs:annotation>
<xs:documentation>
Expand Down Expand Up @@ -285,4 +320,4 @@
<xs:enumeration value="desc"/>
</xs:restriction>
</xs:simpleType>
</xs:schema>
</xs:schema>
22 changes: 22 additions & 0 deletions src/Magento/FunctionalTestingFramework/Util/TestGenerator.php
Original file line number Diff line number Diff line change
Expand Up @@ -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 = ']';

Expand Down Expand Up @@ -538,6 +540,7 @@ public function generateStepsPhp($actionObjects, $generationScope = TestGenerato
$dependentSelector = null;
$visible = null;
$command = null;
$cronGroups = '';
$arguments = null;
$sortOrder = null;
$storeCode = null;
Expand All @@ -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']);
}
Expand Down Expand Up @@ -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());
Expand Down