diff --git a/dev/tests/verification/Resources/ActionGroupWithStepKeyReferences.txt b/dev/tests/verification/Resources/ActionGroupWithStepKeyReferences.txt
index eddaaf784..d1cae1da4 100644
--- a/dev/tests/verification/Resources/ActionGroupWithStepKeyReferences.txt
+++ b/dev/tests/verification/Resources/ActionGroupWithStepKeyReferences.txt
@@ -45,7 +45,7 @@ class ActionGroupWithStepKeyReferencesCest
$I->fillField($action1); // stepKey: action1ActionGroup
$I->comment("Invocation stepKey will be appended in non stepKey instances");
$action3ActionGroup = $I->executeJS($action3ActionGroup); // stepKey: action3ActionGroup
- $action4ActionGroup = $I->magentoCLI($action4ActionGroup, "\"stuffHere\""); // stepKey: action4ActionGroup
+ $action4ActionGroup = $I->magentoCLI($action4ActionGroup, 60, "\"stuffHere\""); // stepKey: action4ActionGroup
$I->comment($action4ActionGroup);
$date = new \DateTime();
$date->setTimestamp(strtotime("{$action5}"));
diff --git a/dev/tests/verification/Resources/BasicFunctionalTest.txt b/dev/tests/verification/Resources/BasicFunctionalTest.txt
index f82c84119..bc7fe0b94 100644
--- a/dev/tests/verification/Resources/BasicFunctionalTest.txt
+++ b/dev/tests/verification/Resources/BasicFunctionalTest.txt
@@ -126,8 +126,14 @@ class BasicFunctionalTestCest
$grabMultipleKey1 = $I->grabMultiple(".functionalTestSelector"); // stepKey: grabMultipleKey1
$grabTextFromKey1 = $I->grabTextFrom(".functionalTestSelector"); // stepKey: grabTextFromKey1
$grabValueFromKey1 = $I->grabValueFrom(".functionalTestSelector"); // stepKey: grabValueFromKey1
- $magentoCli1 = $I->magentoCLI("maintenance:enable", "\"stuffHere\""); // stepKey: magentoCli1
+ $magentoCli1 = $I->magentoCLI("maintenance:enable", 60, "\"stuffHere\""); // stepKey: magentoCli1
$I->comment($magentoCli1);
+ $magentoCli2 = $I->magentoCLI("maintenance:enable", 120, "\"stuffHere\""); // stepKey: magentoCli2
+ $I->comment($magentoCli2);
+ $magentoCli3 = $I->magentoCLISecret("config:set somePath " . CredentialStore::getInstance()->getSecret("someKey"), 60); // stepKey: magentoCli3
+ $I->comment($magentoCli3); // stepKey: magentoCli3
+ $magentoCli4 = $I->magentoCLISecret("config:set somePath " . CredentialStore::getInstance()->getSecret("someKey"), 120); // stepKey: magentoCli4
+ $I->comment($magentoCli4); // stepKey: magentoCli4
$I->makeScreenshot("screenShotInput"); // stepKey: makeScreenshotKey1
$I->maximizeWindow(); // stepKey: maximizeWindowKey1
$I->moveBack(); // stepKey: moveBackKey1
diff --git a/dev/tests/verification/Resources/DataReplacementTest.txt b/dev/tests/verification/Resources/DataReplacementTest.txt
index 731ae63f8..4b4531a4e 100644
--- a/dev/tests/verification/Resources/DataReplacementTest.txt
+++ b/dev/tests/verification/Resources/DataReplacementTest.txt
@@ -52,7 +52,7 @@ class DataReplacementTestCest
$I->searchAndMultiSelectOption("#selector", [msq("uniqueData") . "John", "Doe" . msq("uniqueData")]); // stepKey: parameterArrayReplacementMSQBoth
$I->selectMultipleOptions("#Doe" . msq("uniqueData"), "#element", [msq("uniqueData") . "John", "Doe" . msq("uniqueData")]); // stepKey: multiSelectDataReplacement
$I->fillField(".selector", "0"); // stepKey: insertZero
- $insertCommand = $I->magentoCLI("do something Doe" . msq("uniqueData") . " with uniqueness"); // stepKey: insertCommand
+ $insertCommand = $I->magentoCLI("do something Doe" . msq("uniqueData") . " with uniqueness", 60); // stepKey: insertCommand
$I->comment($insertCommand);
$I->seeInPageSource("StringBefore John StringAfter"); // stepKey: htmlReplace1
$I->seeInPageSource("#John"); // stepKey: htmlReplace2
diff --git a/dev/tests/verification/TestModule/Test/BasicFunctionalTest.xml b/dev/tests/verification/TestModule/Test/BasicFunctionalTest.xml
index ff0708554..c23a3ce60 100644
--- a/dev/tests/verification/TestModule/Test/BasicFunctionalTest.xml
+++ b/dev/tests/verification/TestModule/Test/BasicFunctionalTest.xml
@@ -76,6 +76,9 @@
+
+
+
diff --git a/docs/test/actions.md b/docs/test/actions.md
index 9b125d7fc..c5dc83fdb 100644
--- a/docs/test/actions.md
+++ b/docs/test/actions.md
@@ -1262,6 +1262,7 @@ Attribute|Type|Use|Description
---|---|---|---
`command`|string |optional| CLI command to be executed in Magento environment.
`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.
diff --git a/etc/config/command.php b/etc/config/command.php
index adc372e43..e3b8f1191 100644
--- a/etc/config/command.php
+++ b/etc/config/command.php
@@ -14,6 +14,7 @@
$tokenPassedIn = urldecode($_POST['token'] ?? '');
$command = urldecode($_POST['command'] ?? '');
$arguments = urldecode($_POST['arguments'] ?? '');
+ $timeout = floatval(urldecode($_POST['timeout'] ?? 60));
// Token returned will be null if the token we passed in is invalid
$tokenFromMagento = $tokenModel->loadByToken($tokenPassedIn)->getToken();
@@ -24,7 +25,7 @@
if ($valid) {
$fullCommand = escapeshellcmd($magentoBinary . " $command" . " $arguments");
$process = new Symfony\Component\Process\Process($fullCommand);
- $process->setIdleTimeout(60);
+ $process->setIdleTimeout($timeout);
$process->setTimeout(0);
$idleTimeout = false;
try {
diff --git a/src/Magento/FunctionalTestingFramework/Module/MagentoWebDriver.php b/src/Magento/FunctionalTestingFramework/Module/MagentoWebDriver.php
index 73311e0fa..cbde1ddfc 100644
--- a/src/Magento/FunctionalTestingFramework/Module/MagentoWebDriver.php
+++ b/src/Magento/FunctionalTestingFramework/Module/MagentoWebDriver.php
@@ -511,14 +511,16 @@ public function scrollToTopOfPage()
/**
* Takes given $command and executes it against bin/magento or custom exposed entrypoint. Returns command output.
*
- * @param string $command
- * @param string $arguments
+ * @param string $command
+ * @param integer $timeout
+ * @param string $arguments
* @return string
+ *
* @throws TestFrameworkException
*/
- public function magentoCLI($command, $arguments = null)
+ public function magentoCLI($command, $timeout = null, $arguments = null)
{
- return $this->curlExecMagentoCLI($command, $arguments);
+ return $this->curlExecMagentoCLI($command, $timeout, $arguments);
//TODO: calling bin/magento from pipeline is timing out, needs investigation (ref: MQE-1774)
// try {
// return $this->shellExecMagentoCLI($command, $arguments);
@@ -673,17 +675,18 @@ public function fillSecretField($field, $value)
* The data is decrypted immediately prior to data creation to avoid exposure in console or log.
*
* @param string $command
+ * @param null $timeout
* @param null $arguments
* @throws TestFrameworkException
* @return string
*/
- public function magentoCLISecret($command, $arguments = null)
+ public function magentoCLISecret($command, $timeout = null, $arguments = null)
{
// to protect any secrets from being printed to console the values are executed only at the webdriver level as a
// decrypted value
$decryptedCommand = CredentialStore::getInstance()->decryptAllSecretsInString($command);
- return $this->magentoCLI($decryptedCommand, $arguments);
+ return $this->magentoCLI($decryptedCommand, $timeout, $arguments);
}
/**
@@ -835,20 +838,21 @@ public function makeScreenshot($name = null)
/**
* Takes given $command and executes it against bin/magento executable. Returns stdout output from the command.
*
- * @param string $command
- * @param string $arguments
+ * @param string $command
+ * @param integer $timeout
+ * @param string $arguments
*
* @throws \RuntimeException
* @return string
* @SuppressWarnings(PHPMD.UnusedPrivateMethod)
*/
- private function shellExecMagentoCLI($command, $arguments): string
+ private function shellExecMagentoCLI($command, $timeout, $arguments): string
{
$php = PHP_BINDIR ? PHP_BINDIR . DIRECTORY_SEPARATOR. 'php' : 'php';
$binMagento = realpath(MAGENTO_BP . DIRECTORY_SEPARATOR . 'bin' . DIRECTORY_SEPARATOR . 'magento');
$command = $php . ' -f ' . $binMagento . ' ' . $command . ' ' . $arguments;
$process = new Process(escapeshellcmd($command), MAGENTO_BP);
- $process->setIdleTimeout(60);
+ $process->setIdleTimeout($timeout);
$process->setTimeout(0);
$exitCode = $process->run();
if ($exitCode !== 0) {
@@ -861,12 +865,14 @@ private function shellExecMagentoCLI($command, $arguments): string
/**
* Takes given $command and executes it against exposed MTF CLI entry point. Returns response from server.
*
- * @param string $command
- * @param string $arguments
+ * @param string $command
+ * @param integer $timeout
+ * @param string $arguments
+ *
* @return string
* @throws TestFrameworkException
*/
- private function curlExecMagentoCLI($command, $arguments): string
+ private function curlExecMagentoCLI($command, $timeout, $arguments): string
{
// Remove index.php if it's present in url
$baseUrl = rtrim(
@@ -887,6 +893,7 @@ private function curlExecMagentoCLI($command, $arguments): string
'token' => $restExecutor->getAuthToken(),
getenv('MAGENTO_CLI_COMMAND_PARAMETER') => $command,
'arguments' => $arguments,
+ 'timeout' => $timeout,
],
CurlInterface::POST,
[]
diff --git a/src/Magento/FunctionalTestingFramework/Test/Objects/ActionObject.php b/src/Magento/FunctionalTestingFramework/Test/Objects/ActionObject.php
index 8f60e1e90..ad5ca0c33 100644
--- a/src/Magento/FunctionalTestingFramework/Test/Objects/ActionObject.php
+++ b/src/Magento/FunctionalTestingFramework/Test/Objects/ActionObject.php
@@ -63,6 +63,7 @@ class ActionObject
const DELETE_DATA_MUTUAL_EXCLUSIVE_ATTRIBUTES = ["url", "createDataKey"];
const EXTERNAL_URL_AREA_INVALID_ACTIONS = ['amOnPage'];
const FUNCTION_CLOSURE_ACTIONS = ['waitForElementChange', 'performOn', 'executeInSelenium'];
+ const COMMAND_ACTION_ATTRIBUTES = ['magentoCLI', 'magentoCLISecret'];
const MERGE_ACTION_ORDER_AFTER = 'after';
const MERGE_ACTION_ORDER_BEFORE = 'before';
const ACTION_ATTRIBUTE_TIMEZONE = 'timezone';
@@ -71,6 +72,7 @@ class ActionObject
const ACTION_ATTRIBUTE_VARIABLE_REGEX_PARAMETER = '/\(.+\)/';
const ACTION_ATTRIBUTE_VARIABLE_REGEX_PATTERN = '/({{[\w]+\.[\w\[\]]+}})|({{[\w]+\.[\w]+\((?(?!}}).)+\)}})/';
const STRING_PARAMETER_REGEX = "/'[^']+'/";
+ const DEFAULT_COMMAND_WAIT_TIMEOUT = 60;
const ACTION_ATTRIBUTE_USERINPUT = 'userInput';
const ACTION_TYPE_COMMENT = 'comment';
diff --git a/src/Magento/FunctionalTestingFramework/Test/etc/Actions/customActions.xsd b/src/Magento/FunctionalTestingFramework/Test/etc/Actions/customActions.xsd
index 3a002e126..2424d0d31 100644
--- a/src/Magento/FunctionalTestingFramework/Test/etc/Actions/customActions.xsd
+++ b/src/Magento/FunctionalTestingFramework/Test/etc/Actions/customActions.xsd
@@ -50,6 +50,13 @@
+
+
+
+ Idle timeout in seconds, defaulted to 60s when not specified.
+
+
+
diff --git a/src/Magento/FunctionalTestingFramework/Util/TestGenerator.php b/src/Magento/FunctionalTestingFramework/Util/TestGenerator.php
index 7b0d2916e..fcbb49ac9 100644
--- a/src/Magento/FunctionalTestingFramework/Util/TestGenerator.php
+++ b/src/Magento/FunctionalTestingFramework/Util/TestGenerator.php
@@ -613,7 +613,12 @@ public function generateStepsPhp($actionObjects, $generationScope = TestGenerato
if (isset($customActionAttributes['timeout'])) {
$time = $customActionAttributes['timeout'];
}
- $time = $time ?? ActionObject::getDefaultWaitTimeout();
+
+ if (in_array($actionObject->getType(), ActionObject::COMMAND_ACTION_ATTRIBUTES)) {
+ $time = $time ?? ActionObject::DEFAULT_COMMAND_WAIT_TIMEOUT;
+ } else {
+ $time = $time ?? ActionObject::getDefaultWaitTimeout();
+ }
if (isset($customActionAttributes['parameterArray']) && $actionObject->getType() != 'pressKey') {
// validate the param array is in the correct format
@@ -1282,6 +1287,7 @@ public function generateStepsPhp($actionObjects, $generationScope = TestGenerato
$actor,
$actionObject,
$command,
+ $time,
$arguments
);
$testSteps .= sprintf(self::STEP_KEY_ANNOTATION, $stepKey) . PHP_EOL;