Skip to content

Commit f101b0e

Browse files
authored
Merge pull request #500 from magento/MQE-1257
MQE-1257: MFTF Doctor command
2 parents 6c13d9d + de359cb commit f101b0e

File tree

9 files changed

+435
-20
lines changed

9 files changed

+435
-20
lines changed

dev/tests/functional/standalone_bootstrap.php

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,12 @@
1515

1616
require_once realpath(PROJECT_ROOT . '/vendor/autoload.php');
1717

18+
$envFilePath = dirname(dirname(__DIR__)) . DIRECTORY_SEPARATOR;
19+
defined('ENV_FILE_PATH') || define('ENV_FILE_PATH', $envFilePath);
20+
1821
//Load constants from .env file
19-
$envFilePath = dirname(dirname(__DIR__));
20-
if (file_exists($envFilePath . DIRECTORY_SEPARATOR . '.env')) {
21-
$env = new \Dotenv\Loader($envFilePath . DIRECTORY_SEPARATOR . '.env');
22+
if (file_exists(ENV_FILE_PATH . '.env')) {
23+
$env = new \Dotenv\Loader(ENV_FILE_PATH . '.env');
2224
$env->load();
2325

2426
foreach ($_ENV as $key => $var) {

docs/commands/mftf.md

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,24 @@ You can include options to set configuration parameter values for your environme
109109
vendor/bin/mftf build:project --MAGENTO_BASE_URL=http://magento.local/ --MAGENTO_BACKEND_NAME=admin214365
110110
```
111111

112+
### `doctor`
113+
114+
#### Description
115+
116+
Diagnose MFTF configuration and setup. Currently this command will check the following:
117+
- Verify admin credentials are valid. Allowing MFTF authenticates and runs API requests to Magento through cURL
118+
- Verify that Selenium is up and running and available for MFTF
119+
- Verify that new session of browser can open Magento admin and store front urls
120+
- Verify that MFTF can run MagentoCLI commands
121+
122+
#### Usage
123+
124+
```bash
125+
vendor/bin/mftf doctor
126+
```
127+
128+
#### Options
129+
112130
### `generate:tests`
113131

114132
#### Description

src/Magento/FunctionalTestingFramework/Console/CommandList.php

Lines changed: 14 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -29,19 +29,20 @@ class CommandList implements CommandListInterface
2929
public function __construct(array $commands = [])
3030
{
3131
$this->commands = [
32-
'build:project' => new BuildProjectCommand(),
33-
'reset' => new CleanProjectCommand(),
34-
'generate:urn-catalog' => new GenerateDevUrnCommand(),
35-
'generate:suite' => new GenerateSuiteCommand(),
36-
'generate:tests' => new GenerateTestsCommand(),
37-
'run:test' => new RunTestCommand(),
38-
'run:group' => new RunTestGroupCommand(),
39-
'run:failed' => new RunTestFailedCommand(),
40-
'run:manifest' => new RunManifestCommand(),
41-
'setup:env' => new SetupEnvCommand(),
42-
'upgrade:tests' => new UpgradeTestsCommand(),
43-
'generate:docs' => new GenerateDocsCommand(),
44-
'static-checks' => new StaticChecksCommand()
32+
'build:project' => new BuildProjectCommand(),
33+
'doctor' => new DoctorCommand(),
34+
'generate:docs' => new GenerateDocsCommand(),
35+
'generate:suite' => new GenerateSuiteCommand(),
36+
'generate:tests' => new GenerateTestsCommand(),
37+
'generate:urn-catalog' => new GenerateDevUrnCommand(),
38+
'reset' => new CleanProjectCommand(),
39+
'run:failed' => new RunTestFailedCommand(),
40+
'run:group' => new RunTestGroupCommand(),
41+
'run:manifest' => new RunManifestCommand(),
42+
'run:test' => new RunTestCommand(),
43+
'setup:env' => new SetupEnvCommand(),
44+
'static-checks' => new StaticChecksCommand(),
45+
'upgrade:tests' => new UpgradeTestsCommand(),
4546
] + $commands;
4647
}
4748

Lines changed: 204 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,204 @@
1+
<?php
2+
/**
3+
* Copyright © Magento, Inc. All rights reserved.
4+
* See COPYING.txt for license details.
5+
*/
6+
declare(strict_types = 1);
7+
8+
namespace Magento\FunctionalTestingFramework\Console;
9+
10+
use Codeception\Configuration;
11+
use Symfony\Component\EventDispatcher\EventDispatcher;
12+
use Codeception\SuiteManager;
13+
use Magento\FunctionalTestingFramework\Config\MftfApplicationConfig;
14+
use Magento\FunctionalTestingFramework\Exceptions\TestFrameworkException;
15+
use Symfony\Component\Console\Command\Command;
16+
use Symfony\Component\Console\Input\InputInterface;
17+
use Symfony\Component\Console\Output\OutputInterface;
18+
use Magento\FunctionalTestingFramework\Util\ModuleResolver;
19+
use Magento\FunctionalTestingFramework\Module\MagentoWebDriver;
20+
use Magento\FunctionalTestingFramework\Module\MagentoWebDriverDoctor;
21+
use Symfony\Component\Console\Style\SymfonyStyle;
22+
23+
class DoctorCommand extends Command
24+
{
25+
const CODECEPTION_AUTOLOAD_FILE = PROJECT_ROOT . '/vendor/codeception/codeception/autoload.php';
26+
const MFTF_CODECEPTION_CONFIG_FILE = ENV_FILE_PATH . 'codeception.yml';
27+
const SUITE = 'functional';
28+
29+
/**
30+
* Console output style
31+
*
32+
* @var SymfonyStyle
33+
*/
34+
private $ioStyle;
35+
36+
/**
37+
* Exception Context
38+
*
39+
* @var array
40+
*/
41+
private $context = [];
42+
43+
/**
44+
* Configures the current command.
45+
*
46+
* @return void
47+
*/
48+
protected function configure()
49+
{
50+
$this->setName('doctor')
51+
->setDescription(
52+
'This command checks environment readiness for generating and running MFTF tests.'
53+
);
54+
}
55+
56+
/**
57+
* Executes the current command.
58+
*
59+
* @param InputInterface $input
60+
* @param OutputInterface $output
61+
* @return integer
62+
* @throws TestFrameworkException
63+
* @SuppressWarnings(PHPMD.CyclomaticComplexity)
64+
*/
65+
protected function execute(InputInterface $input, OutputInterface $output)
66+
{
67+
// For output style
68+
$this->ioStyle = new SymfonyStyle($input, $output);
69+
70+
$cmdStatus = true;
71+
72+
// Config application
73+
$verbose = $output->isVerbose();
74+
MftfApplicationConfig::create(
75+
false,
76+
MftfApplicationConfig::GENERATION_PHASE,
77+
$verbose,
78+
MftfApplicationConfig::LEVEL_DEVELOPER,
79+
false
80+
);
81+
82+
// Check authentication to Magento Admin
83+
$status = $this->checkAuthenticationToMagentoAdmin();
84+
$cmdStatus = $cmdStatus && !$status ? false : $cmdStatus;
85+
86+
// Check connection to Selenium
87+
$status = $this->checkContextOnStep(
88+
MagentoWebDriverDoctor::EXCEPTION_CONTEXT_SELENIUM,
89+
'Connecting to Selenium Server'
90+
);
91+
$cmdStatus = $cmdStatus && !$status ? false : $cmdStatus;
92+
93+
// Check opening Magento Admin in web browser
94+
$status = $this->checkContextOnStep(
95+
MagentoWebDriverDoctor::EXCEPTION_CONTEXT_ADMIN,
96+
'Loading Admin page'
97+
);
98+
$cmdStatus = $cmdStatus && !$status ? false : $cmdStatus;
99+
100+
// Check opening Magento Storefront in web browser
101+
$status = $this->checkContextOnStep(
102+
MagentoWebDriverDoctor::EXCEPTION_CONTEXT_STOREFRONT,
103+
'Loading Storefront page'
104+
);
105+
$cmdStatus = $cmdStatus && !$status ? false : $cmdStatus;
106+
107+
// Check access to Magento CLI
108+
$status = $this->checkContextOnStep(
109+
MagentoWebDriverDoctor::EXCEPTION_CONTEXT_CLI,
110+
'Running Magento CLI'
111+
);
112+
$cmdStatus = $cmdStatus && !$status ? false : $cmdStatus;
113+
114+
return $cmdStatus ? 0 : 1;
115+
}
116+
117+
/**
118+
* Check admin account authentication
119+
*
120+
* @return boolean
121+
*/
122+
private function checkAuthenticationToMagentoAdmin()
123+
{
124+
$result = false;
125+
try {
126+
$this->ioStyle->text("Requesting API token for admin user through cURL ...");
127+
ModuleResolver::getInstance()->getAdminToken();
128+
$this->ioStyle->success('Successful');
129+
$result = true;
130+
} catch (TestFrameworkException $e) {
131+
$this->ioStyle->error(
132+
$e->getMessage()
133+
. "\nPlease verify MAGENTO_ADMIN_USERNAME and MAGENTO_ADMIN_PASSWORD in .env."
134+
);
135+
}
136+
return $result;
137+
}
138+
139+
/**
140+
* Check exception context after runMagentoWebDriverDoctor
141+
*
142+
* @param string $exceptionType
143+
* @param string $message
144+
* @return boolean
145+
* @throws TestFrameworkException
146+
*/
147+
private function checkContextOnStep($exceptionType, $message)
148+
{
149+
$this->ioStyle->text($message . ' ...');
150+
$this->runMagentoWebDriverDoctor();
151+
152+
if (isset($this->context[$exceptionType])) {
153+
$this->ioStyle->error($this->context[$exceptionType]);
154+
return false;
155+
} else {
156+
$this->ioStyle->success('Successful');
157+
return true;
158+
}
159+
}
160+
161+
/**
162+
* Run diagnose through MagentoWebDriverDoctor
163+
*
164+
* @return void
165+
* @throws TestFrameworkException
166+
*/
167+
private function runMagentoWebDriverDoctor()
168+
{
169+
if (!empty($this->context)) {
170+
return;
171+
}
172+
173+
$magentoWebDriver = '\\' . MagentoWebDriver::class;
174+
$magentoWebDriverDoctor = '\\' . MagentoWebDriverDoctor::class;
175+
176+
require_once realpath(self::CODECEPTION_AUTOLOAD_FILE);
177+
178+
$config = Configuration::config(realpath(self::MFTF_CODECEPTION_CONFIG_FILE));
179+
$settings = Configuration::suiteSettings(self::SUITE, $config);
180+
181+
// Enable MagentoWebDriverDoctor
182+
$settings['modules']['enabled'][] = $magentoWebDriverDoctor;
183+
$settings['modules']['config'][$magentoWebDriverDoctor] =
184+
$settings['modules']['config'][$magentoWebDriver];
185+
186+
// Disable MagentoWebDriver to avoid conflicts
187+
foreach ($settings['modules']['enabled'] as $index => $module) {
188+
if ($module == $magentoWebDriver) {
189+
unset($settings['modules']['enabled'][$index]);
190+
break;
191+
}
192+
}
193+
unset($settings['modules']['config'][$magentoWebDriver]);
194+
195+
$dispatcher = new EventDispatcher();
196+
$suiteManager = new SuiteManager($dispatcher, self::SUITE, $settings);
197+
try {
198+
$suiteManager->initialize();
199+
$this->context = ['Successful'];
200+
} catch (TestFrameworkException $e) {
201+
$this->context = $e->getContext();
202+
}
203+
}
204+
}

src/Magento/FunctionalTestingFramework/Exceptions/TestFrameworkException.php

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,13 @@
1313
*/
1414
class TestFrameworkException extends \Exception
1515
{
16+
/**
17+
* Exception context
18+
*
19+
* @var array
20+
*/
21+
protected $context;
22+
1623
/**
1724
* TestFrameworkException constructor.
1825
* @param string $message
@@ -27,6 +34,17 @@ public function __construct($message, $context = [])
2734
$context
2835
);
2936

37+
$this->context = $context;
3038
parent::__construct($message);
3139
}
40+
41+
/**
42+
* Return exception context
43+
*
44+
* @return array
45+
*/
46+
public function getContext()
47+
{
48+
return $this->context;
49+
}
3250
}

src/Magento/FunctionalTestingFramework/Module/MagentoWebDriver.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,9 @@
2323
use Symfony\Component\Process\Process;
2424
use Yandex\Allure\Adapter\Support\AttachmentSupport;
2525
use Magento\FunctionalTestingFramework\Exceptions\TestFrameworkException;
26+
use Magento\FunctionalTestingFramework\Config\MftfApplicationConfig;
27+
use Facebook\WebDriver\Remote\RemoteWebDriver;
28+
use Facebook\WebDriver\Exception\WebDriverCurlException;
2629

2730
/**
2831
* MagentoWebDriver module provides common Magento web actions through Selenium WebDriver.

0 commit comments

Comments
 (0)